{"record":{"id":"7134fd2059883341","repo":"gastownhall/beads","slug":"oauth-token-request-returned-status-d-s","errorCode":null,"errorMessage":"oauth: token request returned status %d: %s","messagePattern":"oauth: token request returned status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/oauth.go","lineNumber":145,"sourceCode":"\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tresp, err := m.client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: token request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to read token response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp oauthErrorResponse\n\t\tif json.Unmarshal(body, &errResp) == nil && errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"oauth: token request failed (%s): %s\", errResp.Error, errResp.Description)\n\t\t}\n\t\treturn fmt.Errorf(\"oauth: token request returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar tokenResp oauthTokenResponse\n\tif err := json.Unmarshal(body, &tokenResp); err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to parse token response: %w\", err)\n\t}\n\n\tif tokenResp.AccessToken == \"\" {\n\t\treturn fmt.Errorf(\"oauth: token response missing access_token\")\n\t}\n\n\tm.token = tokenResp.AccessToken\n\tm.expiresAt = m.nowFunc().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)\n\n\tdebug.Logf(\"oauth: acquired token (expires in %ds)\", tokenResp.ExpiresIn)\n\treturn nil\n}\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/oauth.go#L127-L163","documentation":"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.","triggerScenarios":"resp.StatusCode != 200 and either json.Unmarshal into oauthErrorResponse fails or errResp.Error is empty in acquireToken.","commonSituations":"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.","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)."],"exampleFix":"// before\nTokenURL: \"https://linear.app/oauth/token\" // marketing site, not API\n// after\nTokenURL: \"https://api.linear.app/oauth/token\"","handlingStrategy":"try-catch","validationCode":"// Probe the endpoint once at startup\ntokURL := cfg.TokenURL\nif resp, err := http.Post(tokURL, \"application/x-www-form-urlencoded\", nil); err == nil && resp.StatusCode >= 500 {\n    log.Printf(\"warning: token endpoint unhealthy: %d\", resp.StatusCode)\n}","typeGuard":null,"tryCatchPattern":"var statusErr *HTTPStatusError\nif errors.As(err, &statusErr) && statusErr.StatusCode >= 500 {\n    // transient gateway failure — retry with backoff\n}","preventionTips":["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."],"tags":["oauth","http-status","gateway-error","go"],"backgroundTag":"http-502-bad-gateway","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}