gastownhall/beads · error
oauth: token request returned status %d: %s
Error message
oauth: token request returned status %d: %s
What it means
The token endpoint returned a non-200 status whose body was not a recognizable OAuth error JSON (no error field), so the raw status code and body are surfaced verbatim for diagnosis.
Source
Thrown at internal/linear/oauth.go:145
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := m.client.Do(req)
if err != nil {
return fmt.Errorf("oauth: token request failed: %w", err)
}
defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit
if err != nil {
return fmt.Errorf("oauth: failed to read token response: %w", err)
}
if resp.StatusCode != http.StatusOK {
var errResp oauthErrorResponse
if json.Unmarshal(body, &errResp) == nil && errResp.Error != "" {
return fmt.Errorf("oauth: token request failed (%s): %s", errResp.Error, errResp.Description)
}
return fmt.Errorf("oauth: token request returned status %d: %s", resp.StatusCode, string(body))
}
var tokenResp oauthTokenResponse
if err := json.Unmarshal(body, &tokenResp); err != nil {
return fmt.Errorf("oauth: failed to parse token response: %w", err)
}
if tokenResp.AccessToken == "" {
return fmt.Errorf("oauth: token response missing access_token")
}
m.token = tokenResp.AccessToken
m.expiresAt = m.nowFunc().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)
debug.Logf("oauth: acquired token (expires in %ds)", tokenResp.ExpiresIn)
return nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the returned body in the error: HTML content usually means the TokenURL is wrong or a proxy is intercepting.
- Verify TokenURL points at the actual token endpoint (e.g. https://api.linear.app/oauth/token).
- Check for proxy/WAF blocking (502/503/403 with HTML) and allowlist the endpoint.
- Retry with backoff if the status is 5xx (transient upstream outage).
Example fix
// before TokenURL: "https://linear.app/oauth/token" // marketing site, not API // after TokenURL: "https://api.linear.app/oauth/token"
Defensive patterns
Strategy: try-catch
Validate before calling
// Probe the endpoint once at startup
tokURL := cfg.TokenURL
if resp, err := http.Post(tokURL, "application/x-www-form-urlencoded", nil); err == nil && resp.StatusCode >= 500 {
log.Printf("warning: token endpoint unhealthy: %d", resp.StatusCode)
} Try / catch
var statusErr *HTTPStatusError
if errors.As(err, &statusErr) && statusErr.StatusCode >= 500 {
// transient gateway failure — retry with backoff
} Prevention
- Point TokenURL at the real API host, not a website or proxy page.
- Allowlist the token endpoint in proxies/WAFs.
- Retry only on 5xx; treat 4xx as configuration errors.
When it happens
Trigger: resp.StatusCode != 200 and either json.Unmarshal into oauthErrorResponse fails or errResp.Error is empty in acquireToken.
Common situations: Reverse proxy returning HTML 502/503 error pages; Cloudflare challenges; wrong TokenURL hitting a login page (302 followed to HTML); rate-limit pages without OAuth JSON.
Related errors
- oauth: failed to create token request: %w
- oauth: token request failed: %w
- oauth: failed to read token response: %w
- oauth: token request failed (%s): %s
- oauth: failed to parse token response: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7134fd2059883341.
Report an issue: GitHub.