Skip to content

[Bug]: Claude reports "gave up after repeated API errors" for an expired login or usage limit #10320

Description

@vitalyiegorov

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. In Settings → Providers → Claude, set CLAUDE_CONFIG_DIR path to an empty directory, for example /tmp/claude-empty. An empty config dir produces the same authentication_failed assistant event as an expired session; only the CLI's text differs (Not logged in · Please run /login instead of the refresh failure).
  2. Open any project and send a message to a Claude thread.

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 login and 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", and resultUserFacingError in apps/server/src/provider/Layers/ClaudeAdapter.ts maps it to the generic message. The structured authentication_failed assistant event that the CLI sends first is never used.

The usage-limit case ends the same way. A rejected rate_limit_event already produces a warning row (#7165), but the CLI treats the underlying 429 as non-retryable and ends the turn as api_error. The CLI does not stamp blocking_limit on this path, even though T3 maps that reason to a usage-limit message, so the quota case still lands on api_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 dev

Logs or stack traces

# SDK message sequence observed by the adapter (auth case)
assistant  { error: "authentication_failed", message: { content: [{ type: "text", text: "Not logged in · Please run /login" }] } }
result     { subtype: "success", is_error: false, terminal_reason: "api_error" }
# T3 emits: runtime.error "Claude gave up after repeated API errors."

# usage-limit case
system     { subtype: "rate_limit_event", rate_limit_info: { status: "rejected", rateLimitType: "five_hour", resetsAt: ... } }
result     { subtype: "success", terminal_reason: "api_error" }

Screenshots, recordings, or supporting files

Signed-out turn ends with the generic API error

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 same CLAUDE_CONFIG_DIR if 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).

Activity

  1. 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
  2. juliusmarminge commented on Sep 6, 2026

    @juliusmarminge
    Member

    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" (often subtype: "success", empty errors). resultUserFacingError in apps/server/src/provider/Layers/ClaudeAdapter.ts maps 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_failed is not referenced anywhere in the repo
    • rate_limit_event with status: "rejected" — already becomes a warning row (fix(claude): surface usage-limit pauses in the thread #7165); the turn still finishes as the generic api_error string
    • blocking_limit already 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

    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 (or is_error success 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.error and turn.completed.errorMessage, so one adapter change covers every client.

  3. added
    via-triageFiled through npx t3 triage
    bugSomething is broken or behaving incorrectly.
    acceptedfeature request accepted
    on Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions