neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 6f733f4a9bf01d40235774e553f193facc33945f
parent 16680e57bacadbf2b0493e89f5f62e9fb1b69328
Author: Yi Ming <ofseed@foxmail.com>
Date:   Tue,  3 Feb 2026 22:33:14 +0800

fix(lsp): avoid scheduling client deletion before LspNotify #37685

Problem:
`Client.on_exit` runs `Client._on_detach` and the client removal logic
within two separate `vim.schedule` sequentially. However, since
`Client._on_detach` executes `LspNotify` inside `vim.schedule`, this
causes `LspNotify` to be executed after the client removal, which is
scheduled first. At that point, a valid `Client` can no longer be
retrieved within the autocmd callback.

Solution:
Put the client deletion inside the `vim.schedule` call.
Diffstat:
Mruntime/lua/vim/lsp/client.lua | 44++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua @@ -1358,6 +1358,28 @@ function Client:_on_exit(code, signal) reset_defaults(bufnr) end end + + -- Schedule the deletion of the client object + -- so that it exists in the execution of autocommands + vim.schedule(function() + all_clients[self.id] = nil + + -- Client can be absent if executable starts, but initialize fails + -- init/attach won't have happened + if self then + changetracking.reset(self) + end + if code ~= 0 or (signal ~= 0 and signal ~= 15) then + local msg = string.format( + 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', + self and self.name or 'unknown', + code, + signal, + log.get_filename() + ) + vim.notify(msg, vim.log.levels.WARN) + end + end) end) if self._handle_restart ~= nil then @@ -1365,28 +1387,6 @@ function Client:_on_exit(code, signal) self._handle_restart = nil end - -- Schedule the deletion of the client object so that it exists in the execution of LspDetach - -- autocommands - vim.schedule(function() - all_clients[self.id] = nil - - -- Client can be absent if executable starts, but initialize fails - -- init/attach won't have happened - if self then - changetracking.reset(self) - end - if code ~= 0 or (signal ~= 0 and signal ~= 15) then - local msg = string.format( - 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', - self and self.name or 'unknown', - code, - signal, - log.get_filename() - ) - vim.notify(msg, vim.log.levels.WARN) - end - end) - self:_run_callbacks( self._on_exit_cbs, lsp.client_errors.ON_EXIT_CALLBACK_ERROR,