CLAUDE_CODE_OAUTH_TOKEN from claude setup-token returns 401 Invalid bearer token on every API call
Bug report: CLAUDE_CODE_OAUTH_TOKEN from claude setup-token returns 401 Invalid bearer token on every API call
Summary
claude setup-token completes successfully and prints a long-lived OAuth token tied to my Claude subscription. Setting that token via the documented CLAUDE_CODE_OAUTH_TOKEN environment variable causes claude auth status to report a logged-in state, but every actual API call — including a minimal claude -p "..." headless call — fails immediately with:
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid bearer token"}}
Environment
- Claude Code CLI version: 2.1.214
- Platform: macOS (MacBook Air)
- Install path:
~/.local/bin/claude(only one CLI install on PATH — verified withwhich claude) - Plan: Claude Max (confirmed via account UI; regenerated the token after first accidentally authenticating
setup-tokenagainst a different, non-Max account in the browser — the correct-account token still fails identically) - Auth method reported:
claude auth status→{"loggedIn": true, "authMethod": "oauth_token", "apiProvider": "firstParty"}
Steps to reproduce
claude setup-token— completes, prints "Long-lived authentication token created successfully!" and a token.- Copy the token via
pbpastedirectly to a file (bypassing any terminal/editor paste step) to rule out transcription error. export CLAUDE_CODE_OAUTH_TOKEN=$(cat <token-file>)claude auth status→ reportsloggedIn: true.claude -p "Reply with exactly the two words: token works"→ fails with401 Invalid bearer token.
Debug evidence
Ran with -d api --debug-file, in a plain terminal with no parent Claude Code process involved. Redacted excerpt:
[DEBUG] Policy limits: fetch failed (auth 401) — Request failed with status code 401
[ERROR] API error (attempt 1/11): 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid bearer token"},"request_id":"req_011CdovoWAFBoeptebW8T1x9"}
[ERROR] API error (attempt 2/11): 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid bearer token"},"request_id":"req_011CdovoZhpUuevZmB1qqPn6"}
[ERROR] Error in API request: 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid bearer token"},"request_id":"req_011CdovoZhpUuevZmB1qqPn6"}
[ERROR] API auth_error: Invalid bearer token
Two concrete request_id values from failed calls, for server-side log lookup:
req_011CdovoWAFBoeptebW8T1x9req_011CdovoZhpUuevZmB1qqPn6
(A separate, nested test run from inside an active Claude Code session additionally surfaced SDK getOAuthToken callback failed: Tool permission stream closed before response received — that specific error is believed to be an artifact of the nested-session test setup, not the root cause, since it does not appear in the plain-terminal reproduction above, which fails with a clean, direct 401 with no internal SDK complication.)
What has been ruled out, each with a direct test
- Copy/paste corruption — token saved via
pbpastedirectly to a file with no editor involved; confirmed single-line, plausible length (127 bytes), no embedded line breaks. - Conflicting environment variables —
env | grep -i anthropicshows nothing set exceptCLAUDE_CODE_OAUTH_TOKENitself; noANTHROPIC_API_KEYorANTHROPIC_AUTH_TOKEN. apiKeyHelperoverride — not configured in any settings file.- Binary/version mismatch —
which claudeandclaude --versionconfirm a single install used throughout, both when generating the token and when testing it. - System clock skew — local clock reads correctly, no evidence of drift.
- Sandbox/permission-prompt configuration — reproduced identically with all local sandbox restrictions fully disabled; this is a pure client/server auth failure, unrelated to local tool permissions.
- Wrong account — first token was generated against the wrong (non-Max) browser session and discarded; regenerated against the correct Max-plan account with an identical result.
Related existing report
This matches the symptom described in issue #48996 ("401 Invalid Bearer Token Immediately After Successful Login") — closed as "not planned" with no resolution, and notably described there as machine- or account-instance-specific rather than account-wide (a colleague on the same organization account, different machine, had no issue). This report adds a second, independently reproduced, more thoroughly eliminated case of what looks like the same underlying defect, via the setup-token / CLAUDE_CODE_OAUTH_TOKEN path specifically rather than interactive /login.
Impact
This blocks the documented, intended use case for CLAUDE_CODE_OAUTH_TOKEN — authenticating headless/automated Claude Code runs (e.g., via cron or launchd) against a Pro/Max subscription rather than separate API-key billing. Right now there appears to be no working path to unattended, subscription-billed Claude Code execution on this machine.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗