From 6f733f4a9bf01d40235774e553f193facc33945f Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 3 Feb 2026 22:33:14 +0800 Subject: [PATCH] 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. --- runtime/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 index 8c5e4082d9..7ec863f7fd 100644 --- 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,