{"record":{"id":"f5e6723380e8161e","repo":"charmbracelet/crush","slug":"failed-to-decode-lsps-w","errorCode":null,"errorMessage":"failed to decode LSPs: %w","messagePattern":"failed to decode LSPs: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":309,"sourceCode":"\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}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to get MCP states: status code %d\", rsp.StatusCode)\n\t}\n\tvar states map[string]proto.MCPClientInfo\n\tif err := json.NewDecoder(rsp.Body).Decode(&states); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode MCP states: %w\", err)\n\t}","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L291-L327","documentation":"GetLSPs received a 200 response but json.Decode failed to unmarshal the body into map[string]proto.LSPClientInfo. The payload did not match the expected LSP client state schema.","triggerScenarios":"Calling GetLSPs when the server returns unexpected JSON: an array instead of an object, a field type change in LSPClientInfo after a version bump, an empty/garbage body, or a proxy substituting content.","commonSituations":"Client/server skew after upgrading one side; middlewares (auth proxies, injectors) rewriting responses; server bug emitting partial JSON for many LSPs.","solutions":["Match client and server versions; rebuild the client against the current proto package.","Capture the raw body and log it alongside the decode error to identify schema drift.","Bypass or reconfigure proxies that may alter the response body.","Restart the server if truncation is suspected."],"exampleFix":"// before\nvar lsps map[string]proto.LSPClientInfo\nif err := json.NewDecoder(rsp.Body).Decode(&lsps); err != nil { return nil, err }\n// after\nbody, _ := io.ReadAll(rsp.Body)\nvar lsps map[string]proto.LSPClientInfo\nif err := json.Unmarshal(body, &lsps); err != nil {\n    return nil, fmt.Errorf(\"failed to decode LSPs: %w (body: %.200s)\", err, body)\n}","handlingStrategy":"type-guard","validationCode":"body, _ := io.ReadAll(rsp.Body)\nvar probe map[string]json.RawMessage\nif err := json.Unmarshal(body, &probe); err != nil { return fmt.Errorf(\"LSPs payload is not an object: %w\", err) }","typeGuard":"func isLSPClientInfoMap(v any) (map[string]proto.LSPClientInfo, bool) {\n\tm, ok := v.(map[string]proto.LSPClientInfo)\n\treturn m, ok && v != nil\n}","tryCatchPattern":"var lsps map[string]proto.LSPClientInfo\nif err := json.Unmarshal(body, &lsps); err != nil {\n\treturn nil, fmt.Errorf(\"unexpected LSPs payload (server/client version mismatch?): %w\", err)\n}","preventionTips":["Upgrade client and server together — proto structs must match the wire format.","Probe with map[string]json.RawMessage when debugging schema drift.","Log the first ~200 bytes of any body that fails to decode.","Disable response-modifying proxies in dev environments."],"tags":["json","decode","lsp","go"],"backgroundTag":"json-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}