From 80c3f03f62c29207d6f3b783171058e615babfe6 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 19 Jun 2026 22:19:22 +0100 Subject: [PATCH] fix(lsp): non-blocking response dispatch to avoid client deadlock readLoop sent the response on the pending channel while holding c.mu. A misbehaving server sending a duplicate response for the same id would block on the buffered(1) channel under the lock, deadlocking all future dispatches. Use a non-blocking send (matching the notification path). --- internal/lsp/client.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 21393d3..9d93b02 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -321,7 +321,14 @@ func (c *Client) readLoop() { if err := json.Unmarshal(body, &resp); err == nil && resp.ID != 0 { c.mu.Lock() if ch, ok := c.pending[resp.ID]; ok { - ch <- &resp + // Non-blocking send: the pending channel is buffered(1) and read + // exactly once. A misbehaving server that sends a duplicate + // response for the same id must not block here while holding c.mu + // (which would deadlock all future dispatches). + select { + case ch <- &resp: + default: + } } c.mu.Unlock() continue