From 0a2a3feb6da2f665e5d590736bc28fce1bd1d38b Mon Sep 17 00:00:00 2001 From: Austin Cherry Date: Fri, 20 Feb 2026 07:09:32 -0600 Subject: [PATCH] fix(lsp): prevent nil client from being stored in clients map (#2262) --- internal/lsp/manager.go | 42 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/internal/lsp/manager.go b/internal/lsp/manager.go index 44f04e9a6e005a9217b4f62e3f3b477f1ff85838..d6b1eaba5498b71c59566c2fbb1df642b6335c6d 100644 --- a/internal/lsp/manager.go +++ b/internal/lsp/manager.go @@ -178,24 +178,38 @@ func (s *Manager) startServer(ctx context.Context, name, filepath string, server return } - // check again in case another goroutine started it in the meantime - var err error - client := s.clients.GetOrSet(name, func() *Client { - var cli *Client - cli, err = New( - ctx, - name, - cfg, - s.cfg.Resolver(), - s.cfg.WorkingDir(), - s.cfg.Options.DebugLSP, - ) - return cli - }) + // Check again in case another goroutine started it in the meantime. + if client, ok := s.clients.Get(name); ok { + switch client.GetServerState() { + case StateReady, StateStarting, StateDisabled: + s.callback(name, client) + return + } + } + + client, err := New( + ctx, + name, + cfg, + s.cfg.Resolver(), + s.cfg.WorkingDir(), + s.cfg.Options.DebugLSP, + ) if err != nil { slog.Error("Failed to create LSP client", "name", name, "error", err) return } + // Only store non-nil clients. If another goroutine raced us, + // prefer the already-stored client. + if existing, ok := s.clients.Get(name); ok { + switch existing.GetServerState() { + case StateReady, StateStarting, StateDisabled: + client.Close(ctx) + s.callback(name, existing) + return + } + } + s.clients.Set(name, client) defer func() { s.callback(name, client) }()