{"record":{"id":"b858bd267f2a6632","repo":"charmbracelet/crush","slug":"failed-to-decode-mcp-states-w","errorCode":null,"errorMessage":"failed to decode MCP states: %w","messagePattern":"failed to decode MCP states: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":326,"sourceCode":"\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}\n\treturn states, nil\n}\n\n// MCPPendingAuth retrieves the MCP servers awaiting OAuth authentication\n// for a workspace.\nfunc (c *Client) MCPPendingAuth(ctx context.Context, id string) ([]proto.MCPPendingAuthServer, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/pending-auth\", id), nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get MCP pending auth: %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 pending auth: status code %d\", rsp.StatusCode)\n\t}\n\tvar pending []proto.MCPPendingAuthServer\n\tif err := json.NewDecoder(rsp.Body).Decode(&pending); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode MCP pending auth: %w\", err)","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L308-L344","documentation":"MCPGetStates received HTTP 200 but the body failed to decode into map[string]proto.MCPClientInfo. The JSON did not match the expected MCP client-state schema — shape drift, truncation, or non-JSON content.","triggerScenarios":"Calling MCPGetStates when the server emits a changed/incompatible MCPClientInfo JSON shape, a proxy injects content, the body is truncated, or an error page slipped through with a 200 status.","commonSituations":"Partial upgrade: new server with old client proto structs (or vice versa); auth proxies adding fields that break strict decoding is rare but HTML injection is common; huge state maps getting truncated by an intermediary.","solutions":["Sync client and server versions so proto.MCPClientInfo matches the server output.","Log the raw response body with the decode error to pinpoint the mismatched field.","Remove/bypass intermediaries that rewrite the body.","Retry after server restart if truncation is the cause."],"exampleFix":"// before\nvar states map[string]proto.MCPClientInfo\nif err := json.NewDecoder(rsp.Body).Decode(&states); err != nil { return nil, err }\n// after\nbody, _ := io.ReadAll(rsp.Body)\nvar states map[string]proto.MCPClientInfo\nif err := json.Unmarshal(body, &states); err != nil {\n    return nil, fmt.Errorf(\"failed to decode MCP states: %w (body: %.200s)\", err, body)\n}","handlingStrategy":"validation","validationCode":"body, _ := io.ReadAll(rsp.Body)\nct := rsp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") || len(bytes.TrimSpace(body)) == 0 {\n\treturn errors.New(\"MCP states response missing or not JSON\")\n}","typeGuard":"func isMCPStatesShape(v any) bool {\n\tm, ok := v.(map[string]proto.MCPClientInfo)\n\treturn ok && m != nil\n}","tryCatchPattern":"var states map[string]proto.MCPClientInfo\nif err := json.Unmarshal(body, &states); err != nil {\n\treturn nil, fmt.Errorf(\"MCP states decode failed; check client/server versions: %w\", err)\n}","preventionTips":["Verify Content-Type before decoding.","Keep proto.MCPClientInfo in sync with the server's JSON schema.","Log raw bodies on failure to spot truncation or HTML injection.","Retry after server restart if large payloads get truncated."],"tags":["json","decode","mcp","go"],"backgroundTag":"json-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}