{"record":{"id":"f4dcdebeb7edcb09","repo":"chenhg5/cc-connect","slug":"usage-endpoint-returned-status-d-s","errorCode":null,"errorMessage":"usage endpoint returned status %d: %s","messagePattern":"usage endpoint returned status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/codex/usage.go","lineNumber":110,"sourceCode":"\nfunc (a *Agent) fetchUsage(ctx context.Context, client *http.Client, tokens codexOAuthTokens) (*core.UsageReport, error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, codexUsageURL, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+tokens.AccessToken)\n\treq.Header.Set(\"ChatGPT-Account-Id\", tokens.AccountID)\n\treq.Header.Set(\"User-Agent\", \"codex-cli\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request usage endpoint: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\n\t\treturn nil, fmt.Errorf(\"usage endpoint returned status %d: %s\", resp.StatusCode, strings.TrimSpace(string(body)))\n\t}\n\n\tvar payload codexUsageResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode usage response: %w\", err)\n\t}\n\n\treturn mapCodexUsage(payload), nil\n}\n\nfunc mapCodexUsage(payload codexUsageResponse) *core.UsageReport {\n\treport := &core.UsageReport{\n\t\tProvider:  \"codex\",\n\t\tAccountID: payload.AccountID,\n\t\tUserID:    payload.UserID,\n\t\tEmail:     payload.Email,\n\t\tPlan:      payload.PlanType,\n\t}","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/usage.go#L92-L128","documentation":"fetchUsage expects the usage endpoint to return HTTP 200. Any other status (401 expired token, 403 wrong account id, 429 rate limit, 5xx) produces this error, which embeds the status code and up to 512 bytes of the response body for diagnosis. The library does not retry non-200 responses; it fails with the server's own diagnostic payload.","triggerScenarios":"resp.StatusCode != http.StatusOK in fetchUsage after a successful request — e.g. access token expired (401), account_id header mismatch (403), server-side throttling (429), or backend outage (500/503). Exercised by TestFetchUsage_HTTPError.","commonSituations":"Long-lived auth.json whose access token expired and needs `codex login` refresh; account_id not matching the token's account; heavy polling triggering 429; transient ChatGPT backend incidents.","solutions":["On 401, re-run `codex login` to refresh the OAuth access token","Read the embedded body in the error message — it usually names the exact problem (invalid token, forbidden account)","On 429, back off / reduce usage-polling frequency before retrying","On 5xx, wait and retry; check ChatGPT/OpenAI status pages for incidents"],"exampleFix":"// before: retries blindly on any failure\nusage, err := agent.GetUsage(ctx)\nif err != nil { time.Sleep(time.Second); retry() }\n// after: branch on status encoded in the error\nusage, err := agent.GetUsage(ctx)\nif err != nil && strings.Contains(err.Error(), \"status 401\") {\n    exec.Command(\"codex\", \"login\").Run() // refresh token\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"_, err := agent.GetUsage(ctx)\nif err != nil {\n    var status int\n    if _, scan := fmt.Sscanf(err.Error(), \"usage endpoint returned status %d\", &status); scan == nil {\n        switch {\n        case status == 401: refreshCodexLogin()\n        case status == 429: backoffAndRetry()\n        case status >= 500: scheduleRetry()\n        }\n    }\n}","preventionTips":["Refresh `codex login` tokens before they expire","Respect rate limits; don't poll usage endpoints aggressively","Read the embedded response body in the error before deciding","Track backend status pages for 5xx windows"],"tags":["go","http","codex","api"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}