fix(auth): classify claude-cli 401 output as oauth refresh failure#98036
fix(auth): classify claude-cli 401 output as oauth refresh failure#98036ly-wang19 wants to merge 2 commits into
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 4, 2026, 9:20 AM ET / 13:20 UTC. Summary PR surface: Source +20, Tests +44. Total +64 across 3 files. Reproducibility: yes. at source level: current main carries CLI provider metadata in FailoverError and the reported Claude CLI 401 text is not classified by the current OAuth matcher. I did not run a live expired-token Claude CLI session in this read-only review. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land one canonical fix that recognizes Claude CLI 401s only with Claude CLI provider context, preserves generic auth handling for other providers, covers positive and non-Claude negative cases, and includes redacted real behavior proof. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main carries CLI provider metadata in FailoverError and the reported Claude CLI 401 text is not classified by the current OAuth matcher. I did not run a live expired-token Claude CLI session in this read-only review. Is this the best way to solve the issue? No, not yet. The current branch is a plausible positive-path fix, but the best fix should use structured provider context instead of inferring Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5ea80c8d80fd. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +20, Tests +44. Total +64 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (2 earlier review cycles)
|
40b474d to
41c572b
Compare
41c572b to
a6778f6
Compare
The Claude CLI subprocess emits its own "Failed to authenticate. API Error: 401 ..." line when its stored OAuth token expires, but isOAuthRefreshFailureMessage only matched OpenClaw's own refresh machinery text. As a result, channel replies fell back to the generic "Something went wrong" failure text and never showed the re-auth hint. Extend the matcher to also recognize the Claude CLI 401 shape. OpenClaw strips ANTHROPIC_API_KEY before spawning the subprocess, so a 401 here is unambiguously an OAuth-expiry signal. Closes openclaw#97553.
a6778f6 to
75fff82
Compare
Mantis Telegram Desktop ProofSummary: Mantis captured native Telegram Desktop before/after GIFs showing Claude CLI auth failure guidance.
Motion-trimmed clips: |
|
Proof refresh for the rebased head What changed:
Local evidence:
GitHub evidence on the same head:
Known status: the PR labels still show the older needs-proof state, but the latest head now has focused regression coverage, autoreview, CI, and native Telegram proof passing. |




What Problem This Solves
When a
claude-cli/*model is active and the Claude CLI's stored OAuth token expires, the subprocess emitsFailed to authenticate. API Error: 401 .... OpenClaw did not recognise that shape, so the channel reply fell back to the generic⚠️ Something went wrong...text and the operator never saw the existing re-auth hint (Re-auth with \openclaw models auth login`, then try again.`).Closes #97553.
Why This Change Was Made
isOAuthRefreshFailureMessageonly matched OpenClaw's own OAuth-refresh machinery text (oauth token refresh failed,access token could not be refreshed,authentication session could not be refreshed automatically). The Claude CLI subprocess emits its own 401 line that none of these patterns match, soclassifyOAuthRefreshFailurereturnednulland the re-auth branch inbuildExternalRunFailureReplywas never reached.OpenClaw strips
ANTHROPIC_API_KEY(viaCLAUDE_CLI_CLEAR_ENV) before spawning the subprocess, so a 401 from the subprocess is unambiguously an OAuth-expiry signal. Recognizingfailed to authenticate+401lets the existing re-auth hint reach channel replies.User Impact
claude-cli/*model whose OAuth token has expired now see the actionable⚠️ Model login failed on the gateway. Please try again. If this keeps happening, re-auth with \openclaw models auth login`.instead of the genericSomething went wrong` failure text.Evidence
src/agents/auth-profiles/oauth-refresh-failure.test.ts:recognizes claude-cli 401 subprocess output as an OAuth refresh failure. It feeds the exact subprocess output from the issue (Failed to authenticate. API Error: 401 Invalid authentication credentials) and assertsclassifyOAuthRefreshFailurereturns a non-null result.openai(provider parse + reason classification) and the typedOAuthRefreshFailureErrorpath still pass.node scripts/run-vitest.mjs oauth-refresh-failure.testexits 0.