From d34cfe1cb83ad839001a9c5dc8ef588686ca2ff9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 14 Jun 2026 20:20:45 +0200 Subject: [PATCH] fix(autoread): handle autocmd errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Any random ftplugin or other autocmd, can throw an error when `:checktime` reloads a buffer. This causes a trace which makes it look like an issue with `autoread.lua`. vim.schedule callback: …/runtime/lua/nvim/autoread.lua:146: FileType Autocommands for "*"..function 1_LoadFTPlugin[20] ..script …/runtime/ftplugin/help.lua: Vim(runtime):E5113: Lua chunk: …/runtime/lua/vim/treesitter.lua:216: Index out of bounds stack traceback: [C]: in function 'nvim_buf_get_text' …/runtime/lua/vim/treesitter.lua:216: in function 'get_node_text' …/runtime/lua/vim/treesitter/query.lua:558: in function 'handler' …/runtime/lua/vim/treesitter/query.lua:843: in function '_match_predicates' …/runtime/lua/vim/treesitter/query.lua:1082: in function '(for generator)' …/runtime/ftplugin/help.lua:91: in function 'runnables' …/runtime/ftplugin/help.lua:124: in main chunk [C]: in function 'checktime' …/runtime/lua/nvim/autoread.lua:146: in function <…/runtime/lua/nvim/autoread.lua:138> Solution: Use pcall() and surface the error via nvim_echo. --- runtime/lua/nvim/autoread.lua | 8 +++++++- test/functional/options/autoread_spec.lua | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/runtime/lua/nvim/autoread.lua b/runtime/lua/nvim/autoread.lua index 2691e8ccc2..5016c43f03 100644 --- a/runtime/lua/nvim/autoread.lua +++ b/runtime/lua/nvim/autoread.lua @@ -140,13 +140,19 @@ local function ensure_watcher(bufnr) set_pending(bufnr, false) return end - vim.cmd.checktime(bufnr) + -- Use pcall: autocmds (e.g. ftplugin) may throw during reload for any reason. + local ok, err = pcall(vim.cmd.checktime, bufnr) ---@type any, any set_pending(bufnr, false) -- On rename events (e.g. atomic save by another editor), the watcher -- is now stale (watching the old inode). Re-establish it. if change_type ~= watch.FileChangeType.Changed then ensure_watcher(bufnr) end + if not ok then + vim.api.nvim_echo({ + { ('autoread: :checktime failed for buffer %d: %s'):format(bufnr, err) }, + }, true, { err = true }) + end end) end) end) diff --git a/test/functional/options/autoread_spec.lua b/test/functional/options/autoread_spec.lua index ad9bc13d2c..d3c7ab2052 100644 --- a/test/functional/options/autoread_spec.lua +++ b/test/functional/options/autoread_spec.lua @@ -199,6 +199,26 @@ describe('autoread file watcher', function() end) end) + it('handles autocmd error during reload', function() + local path = open_watched('original\n') + local bufnr = api.nvim_get_current_buf() + + -- Define a broken autocmd. + n.exec_lua([[ + vim.api.nvim_create_autocmd('FileChangedShellPost', { + callback = function() error('boom from test autocmd') end, + }) + ]]) + + write_file(path, 'changed\n') + + -- autoread should surface the error, and do its cleanup despite the failed autocmd. + retry(nil, 3000, function() + t.matches('autoread:.*boom from test autocmd', n.eval('v:errmsg')) + eq(0, api.nvim_get_option_value('busy', { buf = bufnr })) + end) + end) + it('detects changes after atomic rename (external editor save)', function() local path = open_watched('original\n')