Describe the bug
nixd segfaults (Signal 11/SEGV) during expression evaluation while sending a WorkDoneProgressEnd LSP notification. The crash occurs in llvm::json::isUTF8(llvm::StringRef) called from lspserver::OutboundPort::notify() — the StringRef appears to reference invalid memory (address 0x59d44836c755 is clearly not a valid pointer), causing a null-pointer-adjacent dereference inside libLLVM.so.21.1.
Stack trace (crashing thread 1671726)
#0 0x000078e2986463d9 llvm::json::isUTF8(llvm::StringRef, unsigned long*) (libLLVM.so.21.1 + 0xc463d9)
#1 0x000078e2a1c833c0 lspserver::OutboundPort::notify(llvm::StringRef, llvm::json::Value) (libnixd-lspserver.so + 0x213c0)
#2 0x000078e2a1de21c8 llvm::detail::UniqueFunctionBase<...>::CallImpl<lspserver::LSPServer::mkOutNotifiction<WorkDoneProgressEnd>...> (libnixd.so + 0x1141c8)
#3 0x000078e2a1db20f2 llvm::detail::UniqueFunctionBase<...>::CallImpl<nixd::Controller::evalExprWithProgress(...)...> (libnixd.so + 0xe40f2)
#4 0x000078e2a1de6dcf llvm::detail::UniqueFunctionBase<...>::CallImpl<lspserver::LSPServer::mkOutMethod<...>...> (libnixd.so + 0x118dcf)
#5 0x000078e2a1c8c19d lspserver::LSPServer::onReply(llvm::json::Value, llvm::Expected<llvm::json::Value>) (libnixd-lspserver.so + 0x2a19d)
#6 0x000078e2a1c8320b lspserver::InboundPort::dispatch(llvm::json::Value, lspserver::MessageHandler&) (libnixd-lspserver.so + 0x2120b)
#7 0x000078e2a1c851a9 lspserver::InboundPort::loop(lspserver::MessageHandler&) (libnixd-lspserver.so + 0x231a9)
The main thread (1671677) was in Controller::~Controller() → _M_erase (destroying AttrSetClientProc map entries), waiting on thread join — indicating nixd was shutting down while the eval thread was still trying to send progress notifications through a dying/dead channel.
Kernel log
May 24 17:39:41 hornet kernel: nixd[1671726]: segfault at 59d44836c755 ip 000078e2986463d9 sp 000078e2799ce340 error 4 in libLLVM.so.21.1[c463d9,78e2983b0000+5bd0000] likely on CPU 10 (core 2, socket 0)
May 24 17:39:41 hornet kernel: Code: 25 28 00 00 00 48 89 45 f8 31 c0 48 39 fe 74 41 48 89 f9 48 89 f8 eb 12 66 0f 1f 84 00 00 00 00 00 48 83 c0 01 48 39 c6 74 27 <80> 38 00 79 f2 48 8d 7d f0 48 89 55 e0 48 89 4d f0 48 89 4d e8 e8
The <80> 38 00 instruction is cmp byte [rax], 0 — classic strlen-style scan hitting an invalid pointer.
Configuration
{
"nixd": {
"formatting": { "command": ["nixfmt"] }
}
}
Running inside Neovim via nvim-lspconfig.
To Reproduce
Not reliably reproducible — this is an intermittent crash that occurs during heavy flake evaluation. The flake in question (nix flake check) passes cleanly. This appears to be a race condition where the progress notification callback runs after or during controller teardown.
Steps that may trigger it:
- Open a large NixOS flake in an editor with nixd LSP
- Wait for nixd to begin evaluating (progress notifications fire)
- Crash occurs when the LSP channel disconnects or the controller starts shutting down while eval callbacks are still pending
Expected behavior
nixd should not segfault. Progress notifications should be safely dropped if the outbound channel is no longer valid.
Additional context
- nixd version: 2.9.1
- NixOS 25.05 (Warbler), Linux 6.18.26
- x86_64
- Nix store path:
/nix/store/nq6rgzv6kxs01jncg3b19fdygfnxkjwh-nixd-2.9.1
- No custom nixd config beyond formatting command
- Flakes evaluation with home-manager integration (7 hosts)
Describe the bug
nixdsegfaults (Signal 11/SEGV) during expression evaluation while sending aWorkDoneProgressEndLSP notification. The crash occurs inllvm::json::isUTF8(llvm::StringRef)called fromlspserver::OutboundPort::notify()— theStringRefappears to reference invalid memory (address0x59d44836c755is clearly not a valid pointer), causing a null-pointer-adjacent dereference insidelibLLVM.so.21.1.Stack trace (crashing thread 1671726)
The main thread (1671677) was in
Controller::~Controller()→_M_erase(destroyingAttrSetClientProcmap entries), waiting on thread join — indicating nixd was shutting down while the eval thread was still trying to send progress notifications through a dying/dead channel.Kernel log
The
<80> 38 00instruction iscmp byte [rax], 0— classic strlen-style scan hitting an invalid pointer.Configuration
{ "nixd": { "formatting": { "command": ["nixfmt"] } } }Running inside Neovim via nvim-lspconfig.
To Reproduce
Not reliably reproducible — this is an intermittent crash that occurs during heavy flake evaluation. The flake in question (
nix flake check) passes cleanly. This appears to be a race condition where the progress notification callback runs after or during controller teardown.Steps that may trigger it:
Expected behavior
nixd should not segfault. Progress notifications should be safely dropped if the outbound channel is no longer valid.
Additional context
/nix/store/nq6rgzv6kxs01jncg3b19fdygfnxkjwh-nixd-2.9.1