{"record":{"id":"2bee68861ad9c50c","repo":"charmbracelet/crush","slug":"failed-to-get-mcp-pending-auth-status-code-d","errorCode":null,"errorMessage":"failed to get MCP pending auth: status code %d","messagePattern":"failed to get MCP pending auth: status code (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":340,"sourceCode":"\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)\n\t}\n\treturn pending, nil\n}\n\n// MCPAuthURL retrieves the current OAuth authorization URL for a named MCP\n// server, if a flow is in progress.\nfunc (c *Client) MCPAuthURL(ctx context.Context, id, name string) (string, error) {\n\tq := url.Values{\"name\": []string{name}}\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/auth-url\", id), q, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get MCP auth URL: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L322-L358","documentation":"MCPPendingAuth calls GET /workspaces/{id}/mcp/pending-auth and expects HTTP 200 with a JSON array of proto.MCPPendingAuthServer. When the server returns any other status code (401, 404, 500, 502...), the client wraps the status code in this error and returns no data. It signals the workspace MCP pending-auth lookup was rejected server-side, not a network or decoding failure.","triggerScenarios":"Calling Client.MCPPendingAuth(ctx, id) where the HTTP response from /workspaces/{id}/mcp/pending-auth has StatusCode != http.StatusOK (e.g. invalid or expired workspace id, missing/invalid auth token causing 401, server error, or a proxy returning 502).","commonSituations":"Using a workspace id from a different environment, running against a server that requires auth headers the client isn't sending, the workspace having been deleted so the route 404s, or a load balancer/API gateway returning 5xx during server restarts or downtime.","solutions":["Log the actual status code in the message and check it: 401/403 means fix credentials, 404 means the workspace id is wrong, 5xx means the server is unhealthy.","Verify the workspace id passed to MCPPendingAuth exists and is reachable (list workspaces first or re-fetch it).","Ensure the client is constructed with valid auth (same token/user the server expects) and pointed at the correct server URL/environment.","For transient 5xx, retry with backoff; for 4xx, fix the request before retrying."],"exampleFix":"// before\nservers, err := client.MCPPendingAuth(ctx, \"ws_123\") // id guessed from an old config\n\n// after\nws, err := client.Workspace(ctx) // resolve the real workspace id\nif err != nil {\n    return err\n}\nservers, err := client.MCPPendingAuth(ctx, ws.ID)","handlingStrategy":"retry","validationCode":"// Best-effort preflight: ensure the workspace is resolvable before polling pending auth.\nws, err := client.Workspace(ctx)\nif err != nil {\n    return fmt.Errorf(\"workspace unavailable, skip MCPPendingAuth: %w\", err)\n}\nif ws.ID == \"\" {\n    return errors.New(\"no workspace id available for MCP pending auth\")","typeGuard":null,"tryCatchPattern":"servers, err := client.MCPPendingAuth(ctx, id)\nif err != nil {\n    var apiErr *client.StatusError // if the SDK exposes one; otherwise inspect the message\n    if strings.Contains(err.Error(), \"status code 5\") {\n        // transient: retry with backoff\n        return retryWithBackoff(3, func() error { _, err = client.MCPPendingAuth(ctx, id); return err })\n    }\n    if strings.Contains(err.Error(), \"status code 401\") || strings.Contains(err.Error(), \"status code 403\") {\n        return fmt.Errorf(\"re-authenticate required: %w\", err)\n    }\n    return err\n}","preventionTips":["Always resolve the workspace id from the API instead of hardcoding it in config.","Keep auth tokens refreshed; 401 is the most common status behind this error.","Wrap polling calls in bounded retry with backoff for 5xx only.","Log the status code captured in the error message to triage 4xx vs 5xx quickly."],"tags":["http","mcp","client","status-code"],"backgroundTag":"http-non-200-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}