OAuth refresh token not used when credentials are copied to remote/headless machines
Bug Description
When copying ~/.claude/.credentials.json from an authenticated machine to a remote/headless machine, Claude Code throws a 401 authentication_error ("OAuth token has expired") instead of using the refreshToken present in the credentials file to obtain a new access token.
Steps to Reproduce
- Authenticate Claude Code on a local workstation via
claude login(Max subscription, OAuth flow) - Copy
~/.claude/.credentials.jsonto a remote headless machine (e.g., viascp) - Run
claudeon the remote machine after theaccessTokenhas expired
Expected Behavior
Claude Code should detect the expired accessToken, use the refreshToken to obtain a new access token, and continue normally — the same way it does on the original machine.
Actual Behavior
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"OAuth token has expired. Please obtain a new token or refresh your existing token."},"request_id":"req_011CXc8h3VFKY9jGum2N2rTZ"}
Credentials File Structure
The .credentials.json contains all necessary fields for a refresh:
{
"claudeAiOauth": {
"accessToken": "...",
"refreshToken": "...",
"expiresAt": ...,
"scopes": [...],
"subscriptionType": "max",
"rateLimitTier": "..."
}
}
The refreshToken is present but is not being used when the accessToken expires.
Use Case
Managing multiple headless Linux machines (Proxmox LXCs, Incus containers) where browser-based claude login is not practical. These machines are on a Tailnet and the intended workflow is to authenticate once on a workstation and distribute credentials to headless nodes. The copy-paste authorization code flow on headless machines is unreliable.
Environment
- Source machine: Windows 11, Claude Code (Max subscription, OAuth)
- Target machines: Headless Linux (Debian/Ubuntu LXCs and VMs)
- Network: Tailscale mesh
- Claude Code version: Latest (as of Jan 2026)
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗