Skip to content

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

Description

@riazrahaman

Summary

When claude-cli/claude-sonnet-4-6 (or any claude-cli/* model) is used as the active model and the Claude CLI's stored OAuth token has expired, the user receives the generic error:

⚠️ Something went wrong while processing your request. Please try again, or use /new to start a fresh session.

instead of the specific re-auth hint that already exists in the codebase:

⚠️ Model login expired on the gateway for claude-cli. Re-auth with openclaw models auth login --provider claude-cli, then try again.

Root Cause

There are two gaps in the error-handling chain.

Gap 1 — classifyOAuthRefreshFailure doesn't recognise the Claude CLI's 401 message

oauth-refresh-failure-W3UM3WkM.jsisOAuthRefreshFailureMessage only matches messages generated by openclaw's own OAuth-refresh machinery:

function isOAuthRefreshFailureMessage(message) {
    return lower.includes("oauth token refresh failed") ||
           lower.includes("access token could not be refreshed") ||
           lower.includes("authentication session could not be refreshed automatically");
}

When the claude subprocess has an expired token it emits its own message:

Failed to authenticate. API Error: 401 Invalid authentication credentials

None of the three strings match → classifyOAuthRefreshFailure returns null → the re-auth hint in reply-usage-state-q7j5CVEd.js:1107-1115 is never reached.

Gap 2 — isAuthErrorMessage catches it but isn't wired into the channel reply path

A separate isAuthErrorMessage() function in sanitize-user-facing-text-DLYR-7y_.js has patterns that would match the Claude CLI's output ("authentication", /\b401\b/, "expired"). However, it is only used for failover classification (errors-BmvajW3H.js:654) and TUI display (tui-D1r4yDv-.js:2741) — not inside buildExternalRunFailureReply, so the auth signal never reaches the Telegram/channel reply.

Why this matters structurally

cli-shared-BcJqwrq-.js defines CLAUDE_CLI_CLEAR_ENV, which deliberately strips ANTHROPIC_API_KEY (and all related env vars) before spawning the claude subprocess. This means openclaw statically knows the subprocess can only authenticate via its stored OAuth token. A 401 exit from the subprocess is therefore unambiguously an OAuth expiry — but this knowledge is never used to promote the error to an OAuthRefreshFailureError.

Reproduction

  1. Use claude-cli/claude-sonnet-4-6 as the active model.
  2. Allow the Claude CLI's OAuth token to expire (check ~/.claude/.credentials.jsonclaudeAiOauth.expiresAt).
  3. Send any message via Telegram (or any channel).
  4. Observe: generic "Something went wrong" error instead of the re-auth hint.

Expected Behaviour

⚠️ Model login expired on the gateway for claude-cli.
Re-auth with `claude auth login`, then try again.

Suggested Fix

Two changes:

1. Extend isOAuthRefreshFailureMessage (or add a isClaudeCliAuthFailure sibling) to recognise the Claude CLI's 401 output:

lower.includes("failed to authenticate") && lower.includes("401")

2. In attempt-execution-B_ltmss6.js, since CLAUDE_CLI_CLEAR_ENV guarantees the subprocess uses OAuth, promote a subprocess 401 exit directly to an OAuthRefreshFailureError — so the right hint is shown regardless of whatever exact wording the CLI happens to emit in future versions.

Environment

  • openclaw version: 2026.5.27
  • Claude CLI version: 2.1.191
  • Platform: macOS (darwin)

Metadata

Metadata

Assignees

Labels

P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.no-staleExclude from stale automation

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions