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:
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,