{"record":{"id":"ee148211cab34b69","repo":"gastownhall/beads","slug":"failed-to-get-oauth-token-w","errorCode":null,"errorMessage":"failed to get OAuth token: %w","messagePattern":"failed to get OAuth token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/client.go","lineNumber":214,"sourceCode":"\t\tAPIKey:         c.APIKey,\n\t\tTeamID:         c.TeamID,\n\t\tProjectID:      projectID,\n\t\tEndpoint:       c.Endpoint,\n\t\tHTTPClient:     c.HTTPClient,\n\t\tAuthMode:       c.AuthMode,\n\t\tTokenManager:   c.TokenManager,\n\t\tRateLimitFloor: c.RateLimitFloor,\n\t\trateLimitState: c.rateLimitState,\n\t}\n}\n\n// authHeader returns the Authorization header value for this client.\nfunc (c *Client) authHeader() (string, error) {\n\tswitch c.AuthMode {\n\tcase AuthModeOAuth:\n\t\ttoken, err := c.TokenManager.Token()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to get OAuth token: %w\", err)\n\t\t}\n\t\treturn \"Bearer \" + token, nil\n\tdefault:\n\t\treturn c.APIKey, nil\n\t}\n}\n\n// WithRateLimitFloor returns a new client with the specified rate-limit circuit-breaker floor.\n// When remaining API quota drops below this value, Execute returns ErrRateLimitExhausted.\nfunc (c *Client) WithRateLimitFloor(floor int) *Client {\n\treturn &Client{\n\t\tAPIKey:         c.APIKey,\n\t\tTeamID:         c.TeamID,\n\t\tProjectID:      c.ProjectID,\n\t\tEndpoint:       c.Endpoint,\n\t\tHTTPClient:     c.HTTPClient,\n\t\tAuthMode:       c.AuthMode,\n\t\tTokenManager:   c.TokenManager,","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L196-L232","documentation":"When the Linear client is in OAuth auth mode, authHeader obtains an access token from the client's TokenManager to build the Authorization header. If the token manager cannot produce a token (refresh failure, expired refresh token, network problem reaching the token endpoint), the error is wrapped as 'failed to get OAuth token'. It propagates to executeOnce and createIssueSingleAttempt, which surface it to the caller.","triggerScenarios":"Any API call (Execute / createIssueSingleAttempt) with c.AuthMode == AuthModeOAuth where TokenManager.Token() fails — e.g. expired access token with failed refresh, revoked refresh token, or unreachable OAuth token endpoint.","commonSituations":"Long-running processes whose refresh token expired; revoked OAuth app credentials; corporate proxy blocking the token endpoint; clock skew causing premature token expiry; switching between API-key and OAuth config without updating AuthMode.","solutions":["Re-authenticate the OAuth integration to obtain a fresh refresh token","Check the wrapped (%w) inner error — refresh failure vs network — and fix accordingly","Verify the token endpoint is reachable (proxy/firewall/DNS)","Alternatively configure API-key auth mode if OAuth is not required","Ensure the system clock is correct"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if client.AuthMode == linear.AuthModeOAuth {\n\tif _, err := client.TokenManager.Token(); err != nil {\n\t\treturn fmt.Errorf(\"OAuth not usable: %w\", err)\n\t}\n}","typeGuard":null,"tryCatchPattern":"data, err := client.Execute(ctx, req)\nif err != nil {\n\tvar tokErr *fmt.WrapError // or errors.As on the underlying token error\n\tif errors.Is(err, oauth.ErrTokenExpired) || strings.Contains(err.Error(), \"failed to get OAuth token\") {\n\t\tif rerr := refreshCredentials(ctx); rerr != nil { return rerr }\n\t\tdata, err = client.Execute(ctx, req)\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Refresh tokens proactively before expiry rather than lazily","Re-authenticate when refresh tokens expire instead of retrying indefinitely","Monitor token-endpoint reachability in health checks","Use API-key mode for long-lived unattended processes where OAuth refresh is impractical"],"tags":["auth","oauth","token","linear"],"backgroundTag":"oauth-token-refresh-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}