{"record":{"id":"4ffba19c7904aa59","repo":"charmbracelet/crush","slug":"failed-to-refresh-oauth-token-w","errorCode":null,"errorMessage":"failed to refresh OAuth token: %w","messagePattern":"failed to refresh OAuth token: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":156,"sourceCode":"\tvar result struct {\n\t\tToken   *oauth.Token `json:\"token\"`\n\t\tSuccess bool         `json:\"success\"`\n\t}\n\tif err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {\n\t\treturn nil, false, fmt.Errorf(\"failed to decode import copilot response: %w\", err)\n\t}\n\treturn result.Token, result.Success, nil\n}\n\n// RefreshOAuthToken refreshes an OAuth token for a provider on the\n// server.\nfunc (c *Client) RefreshOAuthToken(ctx context.Context, id string, scope config.Scope, providerID string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/config/refresh-oauth\", id), nil, jsonBody(struct {\n\t\tScope      config.Scope `json:\"scope\"`\n\t\tProviderID string       `json:\"provider_id\"`\n\t}{Scope: scope, ProviderID: providerID}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to refresh OAuth token: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to refresh OAuth token: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// ProjectNeedsInitialization checks if the project needs\n// initialization.\nfunc (c *Client) ProjectNeedsInitialization(ctx context.Context, id string) (bool, error) {\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/project/needs-init\", id), nil, nil)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to check project init: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn false, fmt.Errorf(\"failed to check project init: status code %d\", rsp.StatusCode)","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L138-L174","documentation":"This error wraps a transport-level failure that occurred while POSTing to /workspaces/{id}/config/refresh-oauth. The client could not complete the HTTP request (connection failure, timeout, request construction error), so RefreshOAuthToken aborts before reading any response. The underlying cause is preserved via %w.","triggerScenarios":"Calling Client.RefreshOAuthToken(ctx, id, scope, providerID) when the HTTP POST to /workspaces/{id}/config/refresh-oauth fails at the transport layer: server unreachable, connection reset, TLS error, or context deadline exceeded.","commonSituations":"Server is down or restarted while refreshing an expired provider OAuth token; DNS or proxy misconfiguration in CI; network flaps on laptops switching Wi-Fi; ctx deadline too short for a slow server.","solutions":["Check that the server backing the client is running and reachable at the configured base URL","Retry the refresh; OAuth refresh is idempotent server-side and transient network errors are the most common cause","Inspect the wrapped error (errors.Unwrap / errors.As) to distinguish timeout, connection refused, or TLS problems","Verify the workspace id is valid so the request is not rejected mid-flight"],"exampleFix":"// before\nif err := client.RefreshOAuthToken(ctx, id, scope, providerID); err != nil {\n    return err\n}\n// after\nif err := client.RefreshOAuthToken(ctx, id, scope, providerID); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        return retryWithBackoff(ctx)\n    }\n    return fmt.Errorf(\"refresh oauth: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Before calling, verify reachability\nif u, err := url.Parse(client.BaseURL()); err != nil || u.Host == \"\" {\n    return fmt.Errorf(\"invalid client base URL\")\n}\nconn, err := net.DialTimeout(\"tcp\", u.Host, 3*time.Second)\nif err != nil {\n    return fmt.Errorf(\"server unreachable: %w\", err)\n}\nconn.Close()","typeGuard":"func isTransportError(err error) bool {\n    var netErr net.Error\n    return errors.As(err, &netErr)\n}","tryCatchPattern":"if err := c.RefreshOAuthToken(ctx, id, scope, pid); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        // retry with backoff\n    }\n    return err\n}","preventionTips":["Set generous but bounded context deadlines for OAuth refresh calls","Retry transient network errors with exponential backoff before surfacing to the user","Monitor server health and alert on refresh endpoint unavailability","Pin and verify TLS/proxy configuration in CI environments"],"tags":["http-client","oauth","network"],"backgroundTag":"oauth-token-refresh-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}