Interactive-only 401 "OAuth access token has expired" with valid setup-token on Team org with channelsEnabled — print mode works; DISABLE_NONESSENTIAL_TRAFFIC fixes it
Summary
On a Claude Team-plan org account authenticated via a claude setup-token long-lived OAuth token (CLAUDE_CODE_OAUTH_TOKEN, sk-ant-oat01-…), every interactive-mode turn fails with a 401 (API Error: 401 Invalid authentication credentials on fresh sessions, Please run /login · API Error: 401 OAuth access token has expired. Re-authenticate to continue. on longer-running ones) — while **print mode (claude -p) with the identical token, config dir, binary, and model always succeeds**, including >200k-token requests on [1m] models and --resume <the-failing-conversation> --fork-session.
Setting CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 fully fixes interactive mode — which strongly suggests the failure originates in the auxiliary startup/feature-flag traffic (bootstrap / GrowthBook / channels), not in the messages request itself, and then surfaces as a misleading "token expired / re-login" banner on the turn.
Environment
- Claude Code 2.1.218 (native install), macOS (Darwin 25.5.0)
- Auth: subscription OAuth via
CLAUDE_CODE_OAUTH_TOKENfromclaude setup-token(Team-plan org; account role "user") - The org pushes remote managed settings (
/statusshows "Enterprise managed settings (remote)") withremote-settings.json={"channelsEnabled": true} - Personal accounts (Pro/Max) on the same machine with their own
CLAUDE_CONFIG_DIR+ ownsk-ant-oat01env token: interactive works fine (theirremote-settings.jsonis{})
Discrimination matrix (all with the same token, verified byte-identical via env inspection)
| Invocation | Result |
|---|---|
| claude -p "…" tiny prompt (haiku/opus/opus[1m]) | ✅ |
| claude -p with ~230k-token prompt on claude-opus-4-8[1m] | ✅ |
| claude -p --resume <failing-conversation> --fork-session (196k-token history, its real cwd) | ✅ |
| hermetic env -i replication of the interactive env, print mode | ✅ |
| interactive TUI, fresh session, tiny prompt | ❌ 401 within ~2–12s of the turn |
| interactive TUI + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 | ✅ turn completes |
| interactive TUI, personal (non-org) account, same machine | ✅ |
Timeline note: interactive sessions on the org account worked for the first ~5h after the setup-token was minted, then began failing ~18h in (overnight) and never recovered; the token itself still validates fine in print mode days-of-hours later.
Debug-log evidence (interactive boot, --debug-file)
[ERROR] [Bootstrap] fetchBootstrapData failed: Error: connect ECONNRESET 160.79.104.10:443
[DEBUG] [Claude in Chrome] Disabled: OAuth token has no scope accepted by /api/oauth/validate
(needs user:profile, user:office, or user:ccr_inference; env-var and setup-token
sessions default to user:inference only)
One failing turn also surfaced API Error: 401 The socket connection was closed unexpectedly. For more information, pass 'verbose: true' in the second argument to fetch() — mixed in with the "token expired" banners, pointing at connection-level failures in the auxiliary fetch path rather than a genuinely expired credential.
Suspected mechanism (synthesis — related issues)
- The
fetchBootstrapDataECONNRESET signature matches #62327 (parallel startup fetches racing a freshly-constructed mTLS agent). - Interactive-vs-print 401 asymmetry with a valid OAuth token was previously reported in #25229.
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICdisabling GrowthBook flag evaluation (and with it channel registration,tengu_harbor) is documented in #58383 and #45918 — consistent with the flag "fixing" this by preventing the failing fetch from ever firing.setup-tokentokens carry onlyuser:inference(docs: they "can't establish Remote Control sessions or fetch claude.ai connectors"; see also #22450). With the org remotely enabling channels (channelsEnabled: true), the interactive client appears to attempt channel/connector-gated auxiliary calls that this token can never satisfy — and the resulting failure is surfaced as a 401 on the user's turn, telling them to/loginalthough their credential is valid.
Expected behavior
- A failure in nonessential/auxiliary traffic (bootstrap, GrowthBook, channels registration) should never fail the user's turn or render a "Please run /login · OAuth access token has expired" banner when the inference credential is valid.
- When running with a scope-limited
CLAUDE_CODE_OAUTH_TOKEN(user:inferenceonly), scope-gated features (channels/connectors) should be skipped gracefully — even when org-managed settings enable them — instead of poisoning turn auth. - The error message should distinguish "auxiliary feature X could not authenticate (missing scope)" from "your token is expired".
Workaround
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 (e.g. via the config dir's settings.json env block) restores interactive mode completely — at the documented-plus-undocumented cost of telemetry, error reports, GrowthBook-gated features, and the 1h prompt-cache TTL (#45381), per the coupling catalogued in #69528.