commit 34815777b28091e6a333ccab4ef2834a84248acb
parent 1d57374c41e6c46c41d537c342ce30ad96cdb85b
Author: Olivia Kinnear <git@superatomic.dev>
Date: Tue, 27 Jan 2026 15:17:45 -0600
fix(lsp): remove side-effects if `vim.lsp.enable()` raises an error (#37571)
Problem:
If `vim.lsp.enable()` fails with an error, either because `'*'` is one
of the provided names or because there is an error in a config,
`vim.lsp.enable()` will still have side-effects:
- All names before the one that encountered an error will still be added
to `lsp._enabled_configs`, but the autocommand will not get added or
run.
- Any name which makes `vim.lsp.config[name]` error will be added to
`lsp._enabled_configs`, causing all future calls to `vim.lsp.enable()`
to fail. This will also break `:che vim.lsp`.
Solution:
- Check all names for errors before modifying `lsp._enabled_configs`.
- Check `vim.lsp.config[name]` does not raise an error before enabling
the name.
Diffstat:
2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
@@ -993,6 +993,14 @@ enable({name}, {enable}) *vim.lsp.enable()*
To disable, pass `enable=false`: Stops related clients and servers
(force-stops servers after a timeout, unless `exit_timeout=false`).
+ Raises an error under the following conditions:
+ • `{name}` is not a valid LSP config name (for example, `'*'`).
+ • `{name}` corresponds to an LSP config file which raises an error.
+
+ If an error is raised when multiple names are provided, this function will
+ have no side-effects; it will not enable/disable any configs, including
+ ones which contain no errors.
+
Examples: >lua
vim.lsp.enable('clangd')
vim.lsp.enable({'lua_ls', 'pyright'})
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
@@ -514,6 +514,14 @@ end
--- To disable, pass `enable=false`: Stops related clients and servers (force-stops servers after
--- a timeout, unless `exit_timeout=false`).
---
+--- Raises an error under the following conditions:
+--- - `{name}` is not a valid LSP config name (for example, `'*'`).
+--- - `{name}` corresponds to an LSP config file which raises an error.
+---
+--- If an error is raised when multiple names are provided, this function will
+--- have no side-effects; it will not enable/disable any configs, including
+--- ones which contain no errors.
+---
--- Examples:
---
--- ```lua
@@ -544,10 +552,22 @@ function lsp.enable(name, enable)
validate('name', name, { 'string', 'table' })
local names = vim._ensure_list(name) --[[@as string[] ]]
+
+ -- Check for errors, and abort with no side-effects if there is one.
for _, nm in ipairs(names) do
- if nm == '*' then
- error('Invalid name')
+ if nm:match('%*') then
+ error('LSP config name cannot contain wildcard ("*")')
+ end
+
+ -- Raise error if `lsp.config[nm]` raises an error, instead of waiting for
+ -- the error to be triggered by `lsp_enable_callback()`.
+ if enable ~= false then
+ _ = lsp.config[nm]
end
+ end
+
+ -- Now that there can be no errors, enable/disable all names.
+ for _, nm in ipairs(names) do
lsp._enabled_configs[nm] = enable ~= false and {} or nil
end