ParseError for `textDocument/codeAction` with 1-line file
Zig Version
0.13.0
ZLS Version
0.13.0
Client / Code Editor / Extensions
neovim 10.4 using zls via nvim-lspconfig
Steps to Reproduce and Observed Behavior
In neovim 10.4, using zls via nvim-lspconfig, write a file containing only:
const std = @import("std");
With the BufWritePre callback set to:
local params = vim.lsp.util.make_range_params()
params.context = { only = { "source.organizeImports" } }
-- buf_request_sync defaults to a 1000ms timeout. Depending on your
-- machine and codebase, you may want longer. Add an additional
-- argument after params if you find that you have to write the file
-- twice for changes to be saved.
-- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000)
if client.supports_method("textDocument/codeAction") then
local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params)
for cid, res in pairs(result or {}) do
for _, r in pairs(res.result or {}) do
if r.edit then
local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16"
vim.lsp.util.apply_workspace_edit(r.edit, enc)
end
end
end
end
vim.lsp.buf.format({ async = false })
On write, ZLS panics:
[ERROR][2025-03-04 12:16:52] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "error: (server): failed to process request-54-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:16:54] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "error: (server): failed to process request-63-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:16:56] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "error: (server): failed to process request-70-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "thread 134148 panic: reached unreachable code\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "Unwind information for `exe:0x12de3cc` was not available, trace may be incomplete\n\nthread 134150 panic: reached unreachable code\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770 "rpc" "/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls" "stderr" "Unwind information for `exe:0x12de3cc` was not available, trace may be incomplete\n\n"
And the callback fails:
Error detected while processing BufWritePre Autocommands for "<buffer=24>":
Error executing lua callback: ...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:900: attempt to index local 'client' (a nil value)
stack traceback:
...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:900: in function 'cancel'
...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:970: in function 'buf_request_sync'
/home/ben/.config/nvim/init.lua:481: in function </home/ben/.config/nvim/init.lua:472>
I have found that including any additional lines of code to the file (const decl or function) will not trigger this panic, so it seems to be related to the edge case of a single-line file.
Expected Behavior
No change to the file (code action should do nothing). It should have the same effect as running the code action on a two-line file like:
const std = @import("std");
const json = std.json;
Relevant log output
I have tested this with Zig 0.14.0 and ZLS 0.14.0-dev.412+81407cd and while I did not encounter a panic, ZLS still exited with an error because of a parsing error. After tracing the received messages, the cause is the code action request which looked like this:
{
"params": {
"context": { "only": ["source.organizeImports"] },
"textDocument": { "uri": "file:///home/techatrix/repos/zls/sample.zig" },
"range": {
"start": { "character": 0, "line": 0 },
"end": { "character": 0, "line": 0 }
}
},
"jsonrpc": "2.0",
"id": 5,
"method": "textDocument/codeAction"
}
This is invalid according to the LSP Specification because CodeActionContext must have a diagnostics field.
The following change has fixed the issue for me:
- params.context = { only = { "source.organizeImports" } }
+ params.context = { only = { "source.organizeImports" }, diagnostics = {} }
That certainly fixed the ParseError for me.
- Should ZLS be more tolerant of a missing required field, since other LSPs seem to be this way?
- If it's okay, I'll wait to close this issue once nixpkgs is updated and I confirm the panic is gone.
Thank you for helping me debug this! For the future, what's the best way to enable and check debug logs for ZLS in Neovim? It didn't seem to be in settings, so I was just going to patch ZLS in my Nix config...
diff --git a/build.zig b/build.zig
index 07c35fd..d70a4dc 100644
--- a/build.zig
+++ b/build.zig
@@ -37,7 +37,7 @@ pub fn build(b: *Build) !void {
const single_threaded = b.option(bool, "single-threaded", "Build a single threaded Executable");
const pie = b.option(bool, "pie", "Build a Position Independent Executable");
- const enable_tracy = b.option(bool, "enable_tracy", "Whether tracy should be enabled.") orelse false;
+ const enable_tracy = b.option(bool, "enable_tracy", "Whether tracy should be enabled.") orelse true;
const enable_tracy_allocation = b.option(bool, "enable_tracy_allocation", "Enable using TracyAllocator to monitor allocations.") orelse enable_tracy;
const enable_tracy_callstack = b.option(bool, "enable_tracy_callstack", "Enable callstack graphs.") orelse enable_tracy;
const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match filter") orelse &[0][]const u8{};
And then look in :LspLog?
- Should ZLS be more tolerant of a missing required field, since other LSPs seem to be this way?
To put it briefly, ZLS should fix its own issues and the editor should fix their issues. This situation here is a big unique since the cause is not in neovim itself but the user config. Writing custom handling for code actions instead of vim.lsp.buf.code_action is a comparatively advanced use case though. Workarounds like this come into play when one side can't easily or quickly fix their issue. But in this case, only a single had to be changed.
2. If it's okay, I'll wait to close this issue once nixpkgs is updated and I confirm the panic is gone.
Do you mean that you want to wait until nixpkgs has updated to Zig and ZLS 0.14.0? If so, feel free to do so and report back later.
what's the best way to enable and check debug logs for ZLS in Neovim?
This should be covered by the logging guide for ZLS. Be aware that the zls.log is not available in ZLS 0.13.0. For neovim with 0.13.0, the best option is probably to run ZLS with the --enable-debug-log flag or vim.lsp.set_log_level("debug") in the neovim config and then open :LspLog. Unfortunately, neovim is one of the worst editors it terms of LSP log quality I've seen (but ZLS is also to blame for that).
Also, enabling support for the Tracy profiler is unrelated to log messages.
No panic in Zig/ZLS 0.14.0 tagged releases.