claude setup-token silently fails on headless Linux servers (no system keyring)
Summary
claude setup-token runs the OAuth browser flow successfully and exits cleanly, but no credentials are persisted on a headless Linux server lacking a system keyring. The next claude -p ... invocation still returns Not logged in · Please run /login.
Environment
- claude version: 2.1.126 (also reproduced on 2.1.x older builds)
- OS: Ubuntu 24.04 LTS (server install — no desktop session)
- No keyring daemon installed:
secret-tool,gnome-keyring-daemon,kwalletd6all absent - Same OAuth flow on a desktop Ubuntu host (KDE + kwalletd6 running) succeeds and writes
~/.claude/.credentials.jsonas plain JSON
Reproduction
On a headless Linux VM with no keyring daemon installed:
\\\`bash
$ claude setup-token
Browser flow opens, code is pasted, command exits with no error
$ ls -la ~/.claude/.credentials.json
ls: cannot access '/home/user/.claude/.credentials.json': No such file or directory
$ claude -p "ping"
Not logged in · Please run /login
\\\`
Expected behavior
Either:
- Detect missing keyring at the start of
setup-tokenand warn loudly (\"no keyring backend; will write to ~/.claude/.credentials.json with mode 0600 — OK?\"), then write to FS - Always prefer FS storage on Linux (the desktop host already does this — both desktop and server were on the same client version), making behavior consistent
- At minimum, fail visibly when the keychain write fails so the user knows OAuth didn't actually persist
Currently the silent-fail mode wastes the OAuth browser dance and leaves users guessing why claude -p still says \"not logged in\".
Workaround
The token is account-bound, not device-bound, so copying ~/.claude/.credentials.json from a desktop machine where OAuth landed correctly works:
\\\bash\
scp ~/.claude/.credentials.json server:~/.claude/.credentials.json
ssh server 'chmod 600 ~/.claude/.credentials.json'
\\
After this, claude -p \"ping\" returns the response immediately. Caveat: when OAuth refreshes silently in the desktop's claude session, the server's copy goes stale; needs periodic re-scp.
Why this matters
Anyone deploying Claude Code on a server (CI workers, autonomous-agent runtimes, headless dev VMs, etc.) hits this exact silent-fail. The standard advice — \"run \claude setup-token\\" — fails silently, and the FS fallback only happens to work because the desktop install path differs.
Suggested fix
In the OAuth-persist code path, after the keychain write call:
- Verify the credential is actually retrievable (round-trip read), or
- If keyring backend reports unavailable / no-op, fall through to FS storage with a
chmod 0600and a clear message to the user about the storage location.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗