claude -p fails to authenticate outside an attended Terminal session (401), despite valid credentials and auth status reporting logged in

Status Open
Reported on v2.1.179
Maintainer reply None cached
Activity 0 comments · opened Jul 22, 2026

claude -p fails to authenticate outside an attended Terminal session, despite valid stored credentials

Environment:

  • Claude Code CLI version 2.1.179 (Homebrew Caskroom install, /opt/homebrew/bin/claude)
  • macOS, Apple Silicon
  • Auth: claude.ai Pro subscription via OAuth (Keychain item "Claude Code-credentials"), not an API key

Summary

claude -p reliably fails with a 401 auth error when invoked from any non-interactive-Terminal context — a LaunchAgent, a non-TTY shell, or even a shell wrapped in a bare pseudo-TTY via script — while the identical command with the identical on-disk credentials succeeds every time from an attended Terminal.app session. claude auth status reports a valid logged-in session even in the failing context, so the CLI's local auth-status check and its actual server-side auth outcome disagree with each other.

Steps to reproduce

  1. Authenticate claude interactively in Terminal.app with a claude.ai Pro subscription.
  2. Confirm claude -p "test" --model claude-haiku-4-5 --output-format json succeeds in Terminal.app (is_error: false).
  3. Run the identical command from a non-TTY context — e.g. via launchctl kickstart on a LaunchAgent, or any subprocess/tool invocation with no real terminal attached.
  4. Observe is_error: true, api_error_status: 401, message alternating between "Invalid authentication credentials" and "OAuth access token has expired. Re-authenticate to continue." — with no time elapsed and no explicit logout/re-login in between.

Evidence ruling out simpler explanations

  • Not a stale/expired token: security find-generic-password -s "Claude Code-credentials" shows the keychain item's modification timestamp unchanged across multiple successful interactive runs and multiple failing headless runs — identical bytes every time.
  • Not a keychain ACL/permission issue: directly tested decrypting the same keychain item from inside the failing (LaunchAgent) context — succeeded (exit 0, no error). Decryption access is not the blocker.
  • Not a CLI version mismatch: which claude / claude --version confirm the identical binary and version is used in both the succeeding and failing contexts.
  • Not a missing environment variable: checked presence of ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR, XDG_CONFIG_HOME, CLAUDE_API_KEY — none set in either context.
  • Not simply "no TTY": wrapping the failing command in a pseudo-TTY via script -q /dev/null claude -p ... still fails with the same 401 — so it isn't bare TTY-device presence; it appears to require a genuinely attended Terminal.app session specifically.
  • The clearest anomaly: claude auth status --json, run in the exact same failing context, reports {"loggedIn": true, "authMethod": "claude.ai", "subscriptionType": "pro", ...} — the CLI's own local status check considers the session fully valid, in the same process context where a real -p call just got a genuine 401 from the server.

Secondary issue, likely same root cause

The CLI also triggers a fresh macOS TCC (TransparencyConsent and Control) permission prompt for kTCCServiceSystemPolicyAppData (and, on first run, several others: Documents Folder, Network Volumes, iCloud Drive, File Provider Domain) on every single process launch from a non-Terminal context, rather than once ever. Confirmed via log show --predicate 'process == "tccd"', correlated to the exact second against script log timestamps across several separate runs.

Impact

Any unattended automation built on claude -p with subscription/OAuth auth (as opposed to ANTHROPIC_API_KEY + --bare) is unreliable outside a live Terminal session — intermittent or consistent 401s, with no clear recovery short of manually re-running the identical command interactively, and repeated permission prompts even once "fixed."

Workaround adopted (not a fix)

Migrated the specific automation this was built for off the standalone CLI entirely, onto Claude Code's own Scheduled Tasks feature, which runs inside the desktop app's own session and does not exhibit this failure. This doesn't help anyone whose automation genuinely needs the standalone CLI in a headless context with subscription auth.

View original on GitHub ↗