Claude Code CLI: OAuth token not auto-refreshed on headless server (refreshToken ignored)

Resolved 💬 3 comments Opened Apr 19, 2026 by kubasiak Closed Apr 23, 2026

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

  1. Run claude login interactively on a server, authenticate via browser
  2. Wait for accessToken to expire (~6 hours, expiresAt in .credentials.json)
  3. Run claude -p "hello" --output-format stream-json --verbose --dangerously-skip-permissions
  4. Expected: token auto-refreshes using refreshToken
  5. 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.json has valid refreshToken and subscriptionType: "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).

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗