Summary
Claude agents bound to an OAuth/subscription account fail with 401 Invalid Bearer Token whenever the daemon process was launched with ANTHROPIC_AUTH_TOKEN set in its environment (a common setup when other agents use an Anthropic-compatible proxy such as DashScope/Bailian).
Symptom
Failed to authenticate. API Error: 401 Invalid Bearer Token
{"type":"result","subtype":"success","is_error":true,"api_error_status":401, ... }
The invocation returns instantly with ~0 tokens — auth is rejected before any model call. Multiple cats sharing one OAuth account all fail identically.
Root cause
buildClaudeEnvOverrides() in packages/api/src/domains/cats/services/agents/providers/ClaudeAgentService.ts scrubs inherited credentials for subscription mode, but the list is incomplete:
} else if (mode === 'subscription') {
// Subscription mode must not inherit shell-level Anthropic credentials.
env.ANTHROPIC_API_KEY = null;
env.ANTHROPIC_BASE_URL = null; // resets to api.anthropic.com
env.ANTHROPIC_MODEL = null;
// ... ANTHROPIC_AUTH_TOKEN is NOT cleared
}
ANTHROPIC_AUTH_TOKEN is sent by the Claude CLI as Authorization: Bearer <token>. So when the daemon env has e.g.:
ANTHROPIC_BASE_URL=https://dashscope.aliyuncs.com/apps/anthropic
ANTHROPIC_AUTH_TOKEN=sk-d11… # a DashScope/Bailian key
the subscription child gets ANTHROPIC_BASE_URL reset to api.anthropic.com but keeps the DashScope token, then sends that proxy token as a Bearer credential to Anthropic → 401 Invalid Bearer Token. It silently overrides the otherwise-valid native OAuth/Keychain login.
Note the error wording ("Invalid Bearer Token", not "invalid api key") confirms the ANTHROPIC_AUTH_TOKEN/Bearer path rather than ANTHROPIC_API_KEY/x-api-key.
Reproduction
- Configure one OAuth (
my-claude-account) and one api_key proxy account (DashScope).
- Launch the daemon from a shell that exports
ANTHROPIC_AUTH_TOKEN/ANTHROPIC_BASE_URL for the proxy.
- Message a cat bound to the OAuth account →
401 Invalid Bearer Token. The proxy-account cat works fine.
Test gap
The existing regression test F062: subscription profile clears inherited ANTHROPIC env vars only asserts ANTHROPIC_API_KEY/ANTHROPIC_BASE_URL are cleared — it never set or checked ANTHROPIC_AUTH_TOKEN, which is why this shipped.
Fix
Strip ANTHROPIC_AUTH_TOKEN alongside the other vars in subscription mode, and extend the F062 test to cover it. PR incoming.
Summary
Claude agents bound to an OAuth/subscription account fail with
401 Invalid Bearer Tokenwhenever the daemon process was launched withANTHROPIC_AUTH_TOKENset in its environment (a common setup when other agents use an Anthropic-compatible proxy such as DashScope/Bailian).Symptom
The invocation returns instantly with ~0 tokens — auth is rejected before any model call. Multiple cats sharing one OAuth account all fail identically.
Root cause
buildClaudeEnvOverrides()inpackages/api/src/domains/cats/services/agents/providers/ClaudeAgentService.tsscrubs inherited credentials for subscription mode, but the list is incomplete:ANTHROPIC_AUTH_TOKENis sent by the Claude CLI asAuthorization: Bearer <token>. So when the daemon env has e.g.:the subscription child gets
ANTHROPIC_BASE_URLreset toapi.anthropic.combut keeps the DashScope token, then sends that proxy token as a Bearer credential to Anthropic →401 Invalid Bearer Token. It silently overrides the otherwise-valid native OAuth/Keychain login.Note the error wording ("Invalid Bearer Token", not "invalid api key") confirms the
ANTHROPIC_AUTH_TOKEN/Bearer path rather thanANTHROPIC_API_KEY/x-api-key.Reproduction
my-claude-account) and one api_key proxy account (DashScope).ANTHROPIC_AUTH_TOKEN/ANTHROPIC_BASE_URLfor the proxy.401 Invalid Bearer Token. The proxy-account cat works fine.Test gap
The existing regression test
F062: subscription profile clears inherited ANTHROPIC env varsonly assertsANTHROPIC_API_KEY/ANTHROPIC_BASE_URLare cleared — it never set or checkedANTHROPIC_AUTH_TOKEN, which is why this shipped.Fix
Strip
ANTHROPIC_AUTH_TOKENalongside the other vars in subscription mode, and extend the F062 test to cover it. PR incoming.