Skip to content

fix(auth): classify claude-cli 401 output as oauth refresh failure#98036

Closed
ly-wang19 wants to merge 2 commits into
openclaw:mainfrom
ly-wang19:fix/claude-cli-oauth-failure-hint-97553
Closed

fix(auth): classify claude-cli 401 output as oauth refresh failure#98036
ly-wang19 wants to merge 2 commits into
openclaw:mainfrom
ly-wang19:fix/claude-cli-oauth-failure-hint-97553

Conversation

@ly-wang19

Copy link
Copy Markdown
Contributor

What Problem This Solves

When a claude-cli/* model is active and the Claude CLI's stored OAuth token expires, the subprocess emits Failed 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

isOAuthRefreshFailureMessage only 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, so classifyOAuthRefreshFailure returned null and the re-auth branch in buildExternalRunFailureReply was never reached.

OpenClaw strips ANTHROPIC_API_KEY (via CLAUDE_CLI_CLEAR_ENV) before spawning the subprocess, so a 401 from the subprocess is unambiguously an OAuth-expiry signal. Recognizing failed to authenticate + 401 lets the existing re-auth hint reach channel replies.

User Impact

  • Operators using a 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.
  • Other OAuth providers (openai, etc.) are unchanged — they continue to match through the existing refresh-machinery patterns.

Evidence

  • New test in 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 asserts classifyOAuthRefreshFailure returns a non-null result.
  • Existing tests for openai (provider parse + reason classification) and the typed OAuthRefreshFailureError path still pass.
  • node scripts/run-vitest.mjs oauth-refresh-failure.test exits 0.

@ly-wang19
ly-wang19 requested a review from a team as a code owner June 30, 2026 05:48
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Jun 30, 2026
@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 4, 2026, 9:20 AM ET / 13:20 UTC.

Summary
The branch adds a Claude CLI 401 OAuth matcher, a claude-cli login-command branch, and classifier plus reply-path regression tests.

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.

  • Raw-message provider inference: 1 added. The diff adds one provider identity inference from free-form error text in a shared auth classifier, which can affect auth-provider recovery guidance outside the positive Claude CLI test.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #97553
Summary: This PR is one candidate fix for the open Claude CLI expired OAuth recovery-copy issue; related open PRs overlap the same root cause but are not a reason to close this active branch yet.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Scope the Claude CLI 401 handling to structured provider: claude-cli context and add a non-Claude negative test.
  • [P1] Add redacted real behavior proof from this branch showing the Claude CLI 401 path and final reply text.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body and comments provide unit-test evidence only, with no redacted terminal output, logs, live output, linked artifact, or recording showing this branch's after-fix Claude CLI OAuth failure path. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A live Telegram transcript would materially verify the user-visible recovery reply after a Claude CLI auth failure. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram live: verify an expired claude-cli OAuth failure replies with the Claude CLI re-auth command instead of generic auth copy.

Risk before merge

  • [P1] The raw-message classifier can send non-Claude providers or custom CLIs that emit Failed to authenticate. API Error: 401 ... to Claude/Anthropic CLI login guidance.
  • [P1] The PR still has no redacted terminal output, logs, live output, recording, or linked artifact showing this branch's after-fix Claude CLI OAuth failure path.
  • [P1] Several open PRs target the same linked issue, so maintainers still need to choose one canonical landing path before closing duplicates.

Maintainer options:

  1. Scope Claude CLI matching to provider context (recommended)
    Thread FailoverError provider context into the OAuth classification path or handle Claude CLI 401s structurally before raw-message classification, then add a non-Claude negative test.
  2. Require branch-specific real proof
    Ask the contributor to add redacted terminal output, logs, live output, or a recording from this branch showing the Claude CLI 401 path and resulting re-auth reply.
  3. Choose a canonical duplicate branch
    If maintainers prefer another candidate PR for the linked issue, keep this branch paused until the chosen PR is clean, proven, and ready to land.

Next step before merge

  • [P1] Human/contributor action is needed because the branch still needs a provider-scoped auth routing fix and real behavior proof; automation cannot supply the contributor's runtime proof.

Security
Cleared: The diff changes auth error classification and tests only; no dependency, workflow, secret-storage, permission, or supply-chain surface is added.

Review findings

  • [P2] Keep Claude CLI 401 matching provider-scoped — src/agents/auth-profiles/oauth-refresh-failure.ts:62-63
Review details

Best 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 claude-cli from raw 401 text alone.

Full review comments:

  • [P2] Keep Claude CLI 401 matching provider-scoped — src/agents/auth-profiles/oauth-refresh-failure.ts:62-63
    This prior blocker remains at the current head. buildExternalRunFailureReply has FailoverError.provider available, but this branch returns claude-cli from the raw message alone, so any non-Claude provider or custom CLI with the same Failed to authenticate. API Error: 401 ... text will get Claude/Anthropic CLI login guidance. Gate this branch on provider === "claude-cli" or move the handling to a structured FailoverError path, and add a non-Claude negative test.
    Confidence: 0.87

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5ea80c8d80fd.

Label changes

Label changes:

  • add merge-risk: 🚨 auth-provider: Merging as-is can misroute auth recovery guidance to Claude/Anthropic CLI login for non-Claude providers that emit the same 401 wording.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body and comments provide unit-test evidence only, with no redacted terminal output, logs, live output, linked artifact, or recording showing this branch's after-fix Claude CLI OAuth failure path. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • add mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The changed behavior is visible channel reply text and can be shown in a short Telegram proof even though the implementation lives in shared auto-reply code.

Label justifications:

  • P2: This is a focused user-facing auth-provider recovery bugfix with limited blast radius and no crash, data-loss, or security-bypass signal.
  • merge-risk: 🚨 auth-provider: Merging as-is can misroute auth recovery guidance to Claude/Anthropic CLI login for non-Claude providers that emit the same 401 wording.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body and comments provide unit-test evidence only, with no redacted terminal output, logs, live output, linked artifact, or recording showing this branch's after-fix Claude CLI OAuth failure path. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The changed behavior is visible channel reply text and can be shown in a short Telegram proof even though the implementation lives in shared auto-reply code.
Evidence reviewed

PR surface:

Source +20, Tests +44. Total +64 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 21 1 +20
Tests 2 44 0 +44
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 65 1 +64

What I checked:

  • Repository policy read and applied: Root AGENTS.md and scoped src/agents/AGENTS.md were read fully; auth-provider compatibility, whole-path review, and real behavior proof rules apply to this PR. (AGENTS.md:7, 5ea80c8d80fd)
  • Gitcrawl unavailable; live GitHub fallback used: gitcrawl was not installed, so related-item review used provided context, live gh, and local source/history inspection.
  • Current main still lacks the Claude CLI 401 matcher: Current main's OAuth refresh matcher only recognizes OpenClaw refresh phrases, so the linked issue is not already implemented on main. (src/agents/auth-profiles/oauth-refresh-failure.ts:43, 5ea80c8d80fd)
  • Patch hard-codes provider identity from raw text: The potential merge result returns claude-cli whenever the raw message matches the observed 401 wording, before checking structured FailoverError provider metadata. (src/agents/auth-profiles/oauth-refresh-failure.ts:62, 1e59f28ecc38)
  • Reply path has provider context available but uses raw classifier fallback: The reply path receives error, but only checks structured OAuthRefreshFailureError before falling back to the raw-message classifier that the PR changes. (src/auto-reply/reply/agent-runner-execution.ts:976, 1e59f28ecc38)
  • CLI failures carry structured provider metadata: Current main throws FailoverError with provider, model, reason, and status for CLI subprocess failures, so provider-scoped handling is feasible without raw-message provider inference. (src/agents/cli-runner/execute.ts:1321, 5ea80c8d80fd)

Likely related people:

  • mbelinky: The OAuth refresh failure classifier and channel reauth guidance this PR extends were introduced by the merged OAuth reauth surfacing work. (role: introduced OAuth reauth reply machinery; confidence: high; commits: b77db8c0b610; files: src/agents/auth-profiles/oauth-refresh-failure.ts, src/auto-reply/reply/agent-runner-execution.ts)
  • sjf-oa: The generic provider-authentication reply classifier that shares this failure surface was added in the recent provider auth surfacing PR. (role: recent provider-auth reply contributor; confidence: high; commits: 113d6f3c6483; files: src/auto-reply/reply/provider-request-error-classifier.ts, src/auto-reply/reply/agent-runner-execution.test.ts)
  • steipete: A recent refactor touched the OAuth helper's command-formatting boundary and is adjacent to this patch's helper changes. (role: recent adjacent refactor contributor; confidence: medium; commits: 49cc59b1e88e; files: src/agents/auth-profiles/oauth-refresh-failure.ts, src/shared/markdown-code.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (2 earlier review cycles)
  • reviewed 2026-07-01T19:42:12.325Z sha a6778f6 :: needs real behavior proof before merge. :: [P1] Route Claude CLI 401s before generic auth copy | [P2] Keep Claude CLI 401 detection provider-scoped
  • reviewed 2026-07-04T13:13:10.310Z sha 75fff82 :: needs real behavior proof before merge. :: [P2] Keep Claude CLI 401 matching provider-scoped

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jun 30, 2026
@ly-wang19
ly-wang19 force-pushed the fix/claude-cli-oauth-failure-hint-97553 branch from 40b474d to 41c572b Compare July 1, 2026 18:31
@clawsweeper clawsweeper Bot added the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label Jul 1, 2026
@ly-wang19
ly-wang19 force-pushed the fix/claude-cli-oauth-failure-hint-97553 branch from 41c572b to a6778f6 Compare July 1, 2026 19:29
ly-wang19 added 2 commits July 4, 2026 20:57
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.
@ly-wang19
ly-wang19 force-pushed the fix/claude-cli-oauth-failure-hint-97553 branch from a6778f6 to 75fff82 Compare July 4, 2026 13:08
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. and removed mantis: telegram-visible-proof Mantis should capture Telegram visible proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 4, 2026
@clawsweeper
clawsweeper Bot temporarily deployed to qa-live-shared July 4, 2026 13:23 Inactive
@openclaw-mantis

Copy link
Copy Markdown
Contributor

Mantis Telegram Desktop Proof

Summary: Mantis captured native Telegram Desktop before/after GIFs showing Claude CLI auth failure guidance.

Main screenshot This PR screenshot
Baseline native Telegram Desktop screenshot Candidate native Telegram Desktop screenshot
Main This PR
Baseline native Telegram Desktop proof GIF Candidate native Telegram Desktop proof GIF

Motion-trimmed clips:

Raw QA files: https://artifacts.openclaw.ai/mantis/telegram-desktop/pr-98036/run-28707546368-1/index.json

@ly-wang19

Copy link
Copy Markdown
Contributor Author

Proof refresh for the rebased head 75fff82ba87e6f26c12d005c1d613c5cb8e619d4.

What changed:

  • The Claude CLI subprocess 401 text is now classified before the generic provider-auth fallback, so channel replies no longer collapse to the generic invalid-token message.
  • The recovery guidance keeps the failing surface as claude-cli, but prints the real Anthropic CLI recovery path: claude auth login && openclaw models auth login --provider anthropic --method cli --set-default.

Local evidence:

  • node scripts/run-vitest.mjs src/agents/auth-profiles/oauth-refresh-failure.test.ts src/auto-reply/reply/provider-request-error-classifier.test.ts src/auto-reply/reply/agent-runner-execution.test.ts src/agents/cli-runner/execute.supervisor-capture.test.ts
  • pnpm exec oxfmt --check --threads=1 src/agents/auth-profiles/oauth-refresh-failure.ts src/agents/auth-profiles/oauth-refresh-failure.test.ts src/auto-reply/reply/agent-runner-execution.test.ts
  • git diff --check
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main returned clean after the command guidance issue was fixed.

GitHub evidence on the same head:

  • Real behavior proof: pass
  • Run agentic native Telegram proof: pass
  • QA Smoke CI: pass
  • build-artifacts: pass
  • check-lint, check-prod-types, check-test-types, check-dependencies: pass
  • Security High checks and Critical Quality agent/core-auth checks: pass

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.

@clawsweeper clawsweeper Bot removed mantis: telegram-visible-proof Mantis should capture Telegram visible proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 5, 2026
@sallyom

sallyom commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for working on this. We’re going to use #97669 as the canonical fix for #97553 because it covers the same Claude CLI OAuth-expiry path in the shared OAuth helper and also handles the structured auth-failure case.

This overlaps that fix, so I’m closing this one as superseded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P2 Normal backlog priority with limited blast radius. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-cli auth failure shows generic error instead of re-auth hint when OAuth token is expired

2 participants