Repository navigation
[Bug]: Claude reports "gave up after repeated API errors" for an expired login or usage limit #10320
Description
Activity
- changed the title
[-][Bug]: Claude reports "gave up after repeated API errors" when the real cause is an expired login or a usage limit[/-][+][Bug]: Claude reports "gave up after repeated API errors" for an expired login or usage limit[/+]on Sep 6, 2026 Triage
Confirmed on current main. This is a real mapping bug, not a duplicate of the adjacent Claude auth/limit tickets.
When Claude is signed out or a subscription window rejects the request, the CLI still ends the turn as
terminal_reason: "api_error"(oftensubtype: "success", emptyerrors).resultUserFacingErrorinapps/server/src/provider/Layers/ClaudeAdapter.tsmaps that to “Claude gave up after repeated API errors.” The structured signals are already on the wire and unused for the final error:- assistant
{ error: "authentication_failed", message.content[].text: "Not logged in · Please run /login" }— that text is backfilled as a normal assistant line;authentication_failedis not referenced anywhere in the repo rate_limit_eventwithstatus: "rejected"— already becomes a warning row (fix(claude): surface usage-limit pauses in the thread #7165); the turn still finishes as the genericapi_errorstringblocking_limitalready has its own message, but that is the CLI’s context-window path, not the 429 / quota path
Users then retry or file provider-outage reports for a login or quota problem.
Not a wash of related work
- fix(claude): fail turns when Claude Code is logged out #8869 covers the auth half (plus probe/settings) and is held for human review; it does not auto-close issues and does not cover usage limits
- [Bug]: Claude auth failure renders as plain result text with no re-login affordance #7878 is the older “plain result text / no re-login affordance” report
- [Bug]: Claude Settings reports authenticated while the launched CLI is logged out #7690 is Settings reporting authenticated while the launched CLI is logged out
- [Bug]: Claude thread stays logged out after re-login until its CLI process is reaped #9607 / fix(server): respawn Claude sessions after re-login on expired credentials #9628 is the stale per-thread CLI process after re-login
- fix(claude): surface usage-limit pauses in the thread #7165 / [Bug]: Claude Opus 5 silently stops when usage limit exceeded. Doesn't tell me immediately. #6513 shipped the usage-limit warning row only
This ticket is specifically auth + usage-limit → generic
api_error.Suggested fix
Adapter-only remapping; no contract change. Latch the cause during the turn and, when the result is a generic
api_error(oris_errorsuccess with no better listed error), report that cause instead. Do not override more specific terminal reasons, listed tool errors, or interrupt/cancel. Leave probe/settings (#8869 / #7690) and process respawn (#9628) on their own PRs.#10321 already implements that latch and marks this issue as closed. Review that PR (or an equivalent) rather than expanding #8869.
Web / desktop / mobile all consume
runtime.errorandturn.completed.errorMessage, so one adapter change covers every client.- assistant
- addedvia-triageFiled through npx t3 triageFiled through npx t3 triagebugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.acceptedfeature request acceptedfeature request accepted
on Sep 6, 2026
Before submitting
Area
apps/server
Steps to reproduce
/tmp/claude-empty. An empty config dir produces the sameauthentication_failedassistant event as an expired session; only the CLI's text differs (Not logged in · Please run /logininstead of the refresh failure).The usage-limit variant: exhaust the 5-hour or weekly window on a Claude subscription, then send a message.
Expected behavior
The turn fails and the error names the real cause and the next step: the login expired, run
claude auth loginand resend, or the usage limit was reached and when to retry.Actual behavior
The CLI's own text appears as a plain assistant line ("Failed to authenticate: OAuth session expired and could not be refreshed" or "Not logged in · Please run /login"). The turn then fails with "Claude gave up after repeated API errors." That sentence is T3's own wording: the CLI only reports
terminal_reason: "api_error", andresultUserFacingErrorinapps/server/src/provider/Layers/ClaudeAdapter.tsmaps it to the generic message. The structuredauthentication_failedassistant event that the CLI sends first is never used.The usage-limit case ends the same way. A rejected
rate_limit_eventalready produces a warning row (#7165), but the CLI treats the underlying 429 as non-retryable and ends the turn asapi_error. The CLI does not stampblocking_limiton this path, even though T3 maps that reason to a usage-limit message, so the quota case still lands onapi_error.Both make a login or quota problem look like a provider outage, so users retry or file provider bug reports.
Impact
Major degradation or frequent failure
Version or commit
main @ 4f782be
Environment
macOS 15, Claude Code CLI 2.1.263, Claude subscription login (OAuth), web client via
vp run devLogs or stack traces
Screenshots, recordings, or supporting files
Related: #8869 (open PR for the auth half), #7165 / #6513 (usage-limit warning row), #7878, #7690, #9607 / #9628 (stale process after re-login).
Workaround
Read the plain CLI line above the error. For the login case run
claude auth login(with the sameCLAUDE_CONFIG_DIRif the instance uses a custom one) and start a new thread; the existing thread's CLI process keeps the stale credentials until it is reaped (#9607).