{"record":{"id":"420a9639b26469f9","repo":"charmbracelet/crush","slug":"failed-to-get-lsps-w","errorCode":null,"errorMessage":"failed to get LSPs: %w","messagePattern":"failed to get LSPs: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":301,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get LSP diagnostics: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get LSP diagnostics: status code %d\", rsp.StatusCode)\n\t}\n\tvar diagnostics map[protocol.DocumentURI][]protocol.Diagnostic\n\tif err := json.NewDecoder(rsp.Body).Decode(&diagnostics); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode LSP diagnostics: %w\", err)\n\t}\n\treturn diagnostics, nil\n}\n\n// GetLSPs retrieves the LSP client states for a workspace.\nfunc (c *Client) GetLSPs(ctx context.Context, id string) (map[string]proto.LSPClientInfo, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/lsps\", id), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get LSPs: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get LSPs: status code %d\", rsp.StatusCode)\n\t}\n\tvar lsps map[string]proto.LSPClientInfo\n\tif err := json.NewDecoder(rsp.Body).Decode(&lsps); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode LSPs: %w\", err)\n\t}\n\treturn lsps, nil\n}\n\n// MCPGetStates retrieves the MCP client states for a workspace.\nfunc (c *Client) MCPGetStates(ctx context.Context, id string) (map[string]proto.MCPClientInfo, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/states\", id), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get MCP states: %w\", err)\n\t}","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L283-L319","documentation":"GetLSPs issues GET /workspaces/{id}/lsps and wraps any transport failure from c.get with this message. The request did not produce an HTTP response at all; this is a connectivity problem, not an LSP-state problem. The real cause is wrapped via %w.","triggerScenarios":"Calling GetLSPs(ctx, id) (directly or via LSPGetStates) when the server is unreachable, the URL is wrong, the connection is refused/reset, or ctx is canceled mid-request.","commonSituations":"Daemon not running when a TUI/plugin asks for LSP states; wrong port in client config; machine sleep/resume killing the socket; expired context during long sessions.","solutions":["Inspect the wrapped error and fix the transport cause (start the server, correct the base URL).","Confirm the crush server is running and listening on the expected address.","Verify the workspace id path segment is a valid, live workspace.","Add retry with backoff for transient network errors."],"exampleFix":"// before\nlsps, err := client.GetLSPs(ctx, wsID)\nif err != nil { panic(err) }\n// after\nlsps, err := client.GetLSPs(ctx, wsID)\nif err != nil {\n    log.Printf(\"GetLSPs failed, server may be down: %v\", err)\n    return nil, fmt.Errorf(\"GetLSPs: %w\", err)\n}","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", serverHostPort, 3*time.Second)\nif err != nil { return fmt.Errorf(\"crush server not reachable: %w\", err) }\nconn.Close()","typeGuard":"func isRetryableNetworkErr(err error) bool {\n\tvar ne net.Error\n\tif errors.As(err, &ne) { return true }\n\treturn errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, syscall.ECONNRESET)\n}","tryCatchPattern":"var lsps map[string]proto.LSPClientInfo\nerr := retry.Do(3, backoff.Exponential(200*time.Millisecond), func() error {\n\tvar e error\n\tlsps, e = client.GetLSPs(ctx, wsID)\n\treturn e\n})\nif err != nil { return fmt.Errorf(\"GetLSPs after retries: %w\", err) }","preventionTips":["Wrap polling calls in bounded retry with backoff.","Start/verify the daemon before issuing state queries.","Use a context timeout longer than expected server latency.","Reuse a single HTTP client so keep-alive and errors are observable."],"tags":["network","http-client","lsp","go"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}