[IMPROVEMENT] Claude Code should surface retry state and handle 429s gracefully instead of silent spinner freeze
[IMPROVEMENT] Claude Code should surface retry state and handle 429s gracefully instead of silent spinner freeze
Summary
When Claude Code hits a rate-limited response (x-should-retry: true), it retries silently up to 6 times over ~23 seconds before surfacing a generic error — leaving the user watching an indistinguishable spinner the entire time. This is a pure client-side UX and reliability fix that doesn't require any backend changes.
This was uncovered while investigating #53922. @cnighswonger confirmed the retry sequence with an HTTP-level capture (88 consecutive 429s logged).
---
Current behavior
- Session hits a 429 with
x-should-retry: true - Claude Code retries silently:
0.86s → 1.4s → 2.2s → 3.8s → 6.9s → 8.4s(~23.5s total) - On final failure: "Server is temporarily limiting requests · Rate limited"
- Session appears frozen with no distinction from a normal thinking spinner
The user has no idea whether to wait, cancel, or restart. In practice, most users restart — creating a new session that immediately competes with existing ones and worsens the concurrency pressure.
---
What Claude Code can fix on its own
1. Show retry state in the UI
Once x-should-retry: true is received, replace the spinner with something like:
⏳ Rate-limited — retrying (2/6, next in 2.2s)
This single change eliminates the "is it frozen or thinking?" confusion and stops users from manually restarting sessions unnecessarily.
2. Client-side request queue instead of per-request retry
Instead of each session independently retrying the same 429 window, a small shared queue with (base_delay × 2^attempt) + jitter would:
- Prevent multiple sessions from hammering simultaneously
- Keep sessions alive rather than exhausting their budget and dying
- Surface queue position in the UI: "Queued behind 2 other requests"
3. Persist session state through a rate-limit window
Currently, once retry budget is exhausted, the session is effectively dead. Claude Code should distinguish "rate-limited" from "error" — preserving the session context and offering to resume when the window clears, rather than requiring a full restart.
---
Evidence
From @cnighswonger's HTTP capture (referenced in #53922):
- Retry signal confirmed as
x-should-retry: true(notRetry-After) - 88 consecutive 429s captured in a 15-minute window
- p95 latency 882ms — gateway-level rejection, backend never involved
- Burst triggered by multiple agents resuming simultaneously (wake-from-idle pattern)
All three improvements above are implementable without any API or backend changes — purely in the Claude Code client.
---
Related
- #53922 — root cause (server-side concurrency cap)
- #46037, #38335, #41788 — same symptom reported by other users
---
@cnighswonger — given your HTTP-level captures, you likely have the clearest view of the retry sequence from the wire. Would love your input here, especially on whether the x-should-retry signal carries enough timing information for the client to implement smarter backoff, or if Fix #6 from #53922 (restoring anthropic-ratelimit-* headers on 429s) is a prerequisite for that.
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗