{"record":{"id":"02e8f1c5ca4d2acf","repo":"charmbracelet/crush","slug":"failed-to-authenticate-mcp-w","errorCode":null,"errorMessage":"failed to authenticate MCP: %w","messagePattern":"failed to authenticate MCP: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":378,"sourceCode":"\t}\n\tvar resp proto.MCPAuthResponse\n\tif err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode MCP auth URL: %w\", err)\n\t}\n\treturn resp.AuthURL, nil\n}\n\n// MCPAuthenticate runs the OAuth flow for a named MCP server. The server's\n// local browser is suppressed; the caller is responsible for surfacing the\n// authorization URL (via polling [Client.MCPPendingAuth] / state events)\n// and opening it on the user's machine. The call blocks until the flow\n// completes, fails, or ctx is cancelled.\nfunc (c *Client) MCPAuthenticate(ctx context.Context, id, name string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/auth\", id), nil,\n\t\tjsonBody(proto.MCPNameRequest{Name: name}),\n\t\thttp.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to authenticate MCP: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\tvar e proto.Error\n\t\tif err := json.NewDecoder(rsp.Body).Decode(&e); err == nil && e.Message != \"\" {\n\t\t\treturn fmt.Errorf(\"failed to authenticate MCP: %s\", e.Message)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to authenticate MCP: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// MCPRefreshPrompts refreshes prompts for a named MCP client.\nfunc (c *Client) MCPRefreshPrompts(ctx context.Context, id, name string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/refresh-prompts\", id), nil,\n\t\tjsonBody(struct {\n\t\t\tName string `json:\"name\"`\n\t\t}{Name: name}),","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L360-L396","documentation":"This error is returned by Client.MCPAuthenticate when the HTTP POST to /workspaces/{id}/mcp/auth fails before a response is available. It wraps the underlying transport error (connection failure, DNS resolution, timeout, or context cancellation) with %w, so the root cause is preserved via errors.Unwrap/Is. It means the authentication request never completed successfully at the network layer.","triggerScenarios":"Calling MCPAuthenticate(ctx, id, name) when the server is unreachable, the base URL is misconfigured, the network is down, TLS fails, or the passed ctx is cancelled before the request completes.","commonSituations":"Dev environment with the API server not running; wrong host/port in client configuration; corporate proxy or VPN blocking the request; long-running auth triggering context deadline exceeded; expired session behind an auth proxy returning connection resets.","solutions":["Inspect the wrapped error with errors.Unwrap (or %v of the returned error) to identify the root cause","Verify the MCP server/API endpoint is running and reachable at the configured base URL","Confirm the workspace id and MCP name are correct and the network/proxy allows the request","Retry with a fresh context and adequate timeout if the cause was a cancelled or expired ctx"],"exampleFix":"// before\nerr := client.MCPAuthenticate(ctx, \"ws-123\", \"filesystem\") // ctx already near deadline\n\n// after\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nif err := client.MCPAuthenticate(ctx, \"ws-123\", \"filesystem\"); err != nil {\n    log.Printf(\"mcp auth failed: %v\", err) // wrapped cause is visible\n}","handlingStrategy":"retry","validationCode":"// Pre-flight connectivity check before calling the API\nfunc serverReachable(ctx context.Context, baseURL string) error {\n    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)\n    defer cancel()\n    req, _ := http.NewRequestWithContext(ctx, http.MethodHead, baseURL, nil)\n    resp, err := http.DefaultClient.Do(req)\n    if err != nil {\n        return fmt.Errorf(\"server unreachable: %w\", err)\n    }\n    resp.Body.Close()\n    return nil\n}","typeGuard":"// Narrow the wrapped transport cause\nfunc isTransportErr(err error) bool {\n    var netErr net.Error\n    var urlErr *url.Error\n    return errors.As(err, &netErr) || errors.As(err, &urlErr) || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled)\n}","tryCatchPattern":"// Go has no try/catch; use retry with error unwrapping\nif err := client.MCPAuthenticate(ctx, id, name); err != nil {\n    if errors.Is(err, context.Canceled) {\n        return err // do not retry caller cancellation\n    }\n    if isTransportErr(err) {\n        err = retryWithBackoff(3, func() error { return client.MCPAuthenticate(ctx, id, name) })\n    }\n    return err\n}","preventionTips":["Pass a context with an explicit timeout rather than a background context","Health-check the server base URL at startup before issuing MCP calls","Verify host/port/TLS configuration against the deployed server version","Handle network transitions (VPN/Wi-Fi) by re-running connectivity checks before retries"],"tags":["network","http","mcp","grpc-client","context"],"backgroundTag":"http-request-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}