fix(autocmds): guard debounced cursor action against wiped buffer#519
Merged
OXY2DEV merged 1 commit intoJul 6, 2026
Merged
Conversation
The cursor autocmd (CursorMoved/CursorMovedI) defers its work through a
debounce timer wrapped in vim.schedule_wrap, so the `action` closure runs on
a later event-loop tick than the autocmd that queued it. The buffer id
captured in `args.buf` is validated when the autocmd fires but not when the
scheduled callback finally runs.
If that buffer is wiped during the debounce window (e.g. a transient Telescope
preview or scratch buffer), the captured id becomes dangling. The callback
then reaches actions.render/clear -> renderer.clear -> markdown.clear ->
nvim_buf_clear_namespace with a dead id, throwing:
Invalid buffer id: N
Re-validate `args.buf` at the top of the scheduled closure so it bails out
cleanly when the buffer no longer exists. Behaviour is unchanged for live
buffers; only the wiped-buffer race is short-circuited.
ImmanuelHaffner
added a commit
to ImmanuelHaffner/markview.nvim
that referenced
this pull request
Jul 11, 2026
Merge upstream/main (OXY2DEV) up to 41ecde1 into the fork's dev branch. Incorporated upstream: - 41ecde1 feat(code_blocks): allow `style` to be dynamically changed (fn) - e85f4a3 fix(code_blocks): icons not rendering in block style - 092c087 fix(autocmds): guard debounced cursor action vs wiped buffer (OXY2DEV#519) -- identical to our 270a687; merge reconciles it as a no-op. Conflict: lua/markview/renderers/markdown.lua code-block dispatch. Resolved by keeping our wrap_forces_simple()/block_on_wrap machinery as the wrap authority (needed for block_on_wrap = 'adaptive', which upstream's wrap->"simple" style-fn cannot express). Upstream's dynamic `style` is still honoured: spec.get(eval_args=...) resolves style to a string before dispatch, so a user style-fn works and our adaptive gate runs on top. Net behavioural change vs our dev tip: block-style code blocks now render the language sign glyph in the gutter (from e85f4a3).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Moving the cursor in a Markdown/preview buffer can crash with:
Root cause
The cursor autocmd (
CursorMoved/CursorMovedI) inautocmds.luadefers its work through a debounce timer wrapped invim.schedule_wrap:So the
actionclosure runs on a later event-loop tick than the autocmd that queued it. The buffer id captured inargs.bufis validated when the autocmd fires (state.buf_attached(args.buf)etc.), but not when the scheduled callback finally runs.If that buffer is wiped during the debounce window — e.g. a transient Telescope preview buffer, a scratch buffer, or any
:bwipeout— the captured id becomes dangling. The callback then flows throughactions.render/actions.clear→renderer.clear→markdown.clear→nvim_buf_clear_namespacewith a dead id, and none of those layers re-validate the buffer, so the raw API call throws.Fix
Re-validate
args.bufat the top of the scheduledactionclosure, so it bails out cleanly when the buffer no longer exists:This is the tightest single choke point — it protects the
splitview_render,render, andclearpaths at once, right before anynvim_buf_*call can reach the stale id.Notes