Summary
LSP server notifications are dispatched synchronously on the client's sole read-loop goroutine. If a notification handler calls Client.Call, it waits for a response that only the blocked read loop can receive, causing a deadlock until the call context expires or the connection closes.
Affected code
- internal/lsp/client.go:99-137
n- internal/lsp/client.go:151-180n
Details
Client.readLoop invokes the configured NotificationHandler inline when it receives a server notification. The public handler contract does not prohibit re-entrant client calls. A handler can therefore send a request with Client.Call and block waiting for its response while the same goroutine responsible for reading and delivering that response remains inside the handler.
The current production diagnostics handler does not make a re-entrant call, but future handlers and other package consumers can trigger this behavior.
Suggested fix
Dispatch notifications through a bounded serialized worker so notification ordering is preserved without blocking the protocol reader. Define the callback lifecycle and shutdown behavior, and add a regression test where a notification handler performs a request and receives its response without deadlocking.
Reference
Item AUD-008 from the 2026-07-18 codebase audit (docs/CODEBASE_AUDIT_2026-07-18.md).
Summary
LSP server notifications are dispatched synchronously on the client's sole read-loop goroutine. If a notification handler calls Client.Call, it waits for a response that only the blocked read loop can receive, causing a deadlock until the call context expires or the connection closes.
Affected code
n- internal/lsp/client.go:151-180nDetails
Client.readLoop invokes the configured NotificationHandler inline when it receives a server notification. The public handler contract does not prohibit re-entrant client calls. A handler can therefore send a request with Client.Call and block waiting for its response while the same goroutine responsible for reading and delivering that response remains inside the handler.
The current production diagnostics handler does not make a re-entrant call, but future handlers and other package consumers can trigger this behavior.
Suggested fix
Dispatch notifications through a bounded serialized worker so notification ordering is preserved without blocking the protocol reader. Define the callback lifecycle and shutdown behavior, and add a regression test where a notification handler performs a request and receives its response without deadlocking.
Reference
Item AUD-008 from the 2026-07-18 codebase audit (docs/CODEBASE_AUDIT_2026-07-18.md).