Claude Code CLI: OAuth token not auto-refreshed on headless server (refreshToken ignored)
Description
Claude Code CLI does not auto-refresh OAuth tokens when running on a headless Linux server (GCP VM). The .credentials.json file contains a valid refreshToken, but when the accessToken expires, the CLI returns a 401 instead of refreshing.
Steps to reproduce
- Run
claude logininteractively on a server, authenticate via browser - Wait for
accessTokento expire (~6 hours,expiresAtin.credentials.json) - Run
claude -p "hello" --output-format stream-json --verbose --dangerously-skip-permissions - Expected: token auto-refreshes using
refreshToken - Actual:
401 authentication_error "Invalid authentication credentials"
Environment
- Claude Code version: 2.1.114
- OS: Debian 12 (GCP e2-micro VM, headless, no X/display)
.credentials.jsonhas validrefreshTokenandsubscriptionType: "max"- No browser available (headless server)
Context
Running Claude Code as a backend agent (called via -p mode from a FastAPI app). The CLI works perfectly when the token is fresh, but silently fails after expiry. Manual token refresh via the OAuth endpoint returns 403 (Cloudflare blocks non-browser requests).
Expected behavior
The CLI should auto-refresh the token using the refreshToken in .credentials.json, similar to how the interactive CLI handles it on desktop machines.
Workaround
Currently falling back to a different agent framework (Kenzan) when OAuth is expired, but this means losing access to Claude Code's full toolset (Read, Write, Edit, Bash).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗