[BUG] Parallel Claude Code sessions started right after 5-hour limit resets — first 3–4 work, the rest fail with "Server is temporarily limiting requests (not your usage limit) · Rate limited"
Preflight Checklist
- [x] I have searched existing issues and this specific scenario hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.119)
What's Wrong?
When my 5-hour usage window resets and I resume work by bulk-spawning ~10
Claude Code sessions back-to-back via a script (each running in its own
terminal / git worktree for parallel spec implementation), the first 3–4
sessions start normally, but the next 5–6 sessions fail almost immediately
with:
API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited
The error message itself states it is not the user's usage limit — yet it
consistently triggers only on the later sessions of a rapid-fire parallel
launch right after a quota reset. Waiting and retrying the failed sessions
individually eventually succeeds, which strongly suggests a server-side
concurrency/burst limiter that activates aggressively when many sessions
bootstrap simultaneously.
This is reproducible nearly every single time I follow my normal workflow
(a single command that fans out parallel spec implementation across multiple
worktrees), so this isn't a one-off transient.
What Should Happen?
Either:
- All sessions should be admitted (the message claims it's not a usage-limit
issue), or
- The CLI should transparently back off and retry the bootstrap requests
with jitter instead of surfacing a hard error to the user, or
- The error message should clearly state that this is a concurrency/burst
limit and document how many parallel session bootstraps are supported per
unit of time.
Steps to Reproduce
- Hit the 5-hour usage limit and wait for it to reset.
- Run a script / wrapper command that spawns ~10 \
claude\processes in rapid
succession across separate working directories (git worktrees in my case).
- Observe: the first 3–4 sessions initialize and work normally.
- The next 5–6 sessions return
\API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited\
shortly after startup or on their first prompt.
- Retrying the failed sessions one-by-one with a delay eventually succeeds.
Error Messages/Logs
\\\\
API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited
\\
Claude Model
Opus 4.7 (claude-opus-4-7, 1M context) — same behavior also observed when
mixed with Sonnet sessions.
Is this a regression?
Not sure — this pattern has been happening for several weeks across multiple
Claude Code versions on the same plan.
Claude Code Version
2.1.119
Platform
Claude Max plan (OAuth, not direct API key)
Operating System
Linux (Ubuntu 22.04, kernel 6.8.0-110-generic)
Terminal/Shell
bash, multiple GNOME Terminal windows launched by a single fan-out command
Additional Information
- The defining trigger is **(a) right after a 5-hour reset, (b) on Linux,
(c) bulk-spawning ~10 sessions back-to-back from a single command, where
the later 5–6 reliably fail while the first 3–4 succeed**.
- Related but distinct: #37436 (Windows, MAX100, multi-session quota
consumption), #40273 (rate limiter when spawning >1 agent), #44481 (Agent
Teams 429/529 with concurrent teammates), #53915 / #53531 (same error
message but different trigger). Filing separately because the existing
issues either don't reproduce on Linux or describe a different trigger.
- This is mildly disruptive rather than fully blocking — failed sessions can
be retried after a delay — but it consistently breaks the "fan out to ~10
parallel worktrees right after a reset" workflow that I rely on for
parallel spec implementation.
- Suggested mitigation on the CLI side: stagger session bootstrap requests
with backoff+jitter, or detect this specific server response and queue the
retry transparently instead of failing the session.
11 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Concurrent Session Throttling on Max Plans — Architectural Analysis & Fix Proposal
Reproducing the same pattern reported in #53922, #46037, #38335, #41788, #54750, #8449. After eliminating every plausible client-side cause, the bottleneck is per-account concurrent-stream throttling that does not scale with the plan multiplier. Sharing evidence and concrete fixes.
---
Reproduction
| Field | Value |
|---|---|
| Plan | Max 20x (recently upgraded from Max 5x) |
| Client | Claude Code 2.1.64 (claude-desktop 1.1.5749) |
| Model |
claude-opus-4-7[1m]|| OS | Linux Fedora 43 |
Behavior:
This matches #53922 verbatim ("first 3–4 work, the rest fail") and #46037 ("only 1 session works while quota is unused").
---
Evidence ruling out client / local causes
I rebuilt the local stack to eliminate every plausible client-side bottleneck before concluding it's server-side:
busy_timeout=30000, dispatch handlers wrapped withasyncio.to_threadfor blocking ops. Local benchmark of 4 concurrent tool calls: 66ms → 62ms (1.07× speedup) — concurrency works at the MCP layer.StreamableHTTPSessionManager. Both connect successfully ([LocalMcpServerManager] Connected to mimir (118 tools)). Not a transport issue.uv runwith direct.venv/bin/python3to remove uv's global cache lock. Multi-PID lock file. No single-instance enforcement.thinkingBudgetTokens: 1500in~/.claude/settings.jsonto minimize per-request thinking tokens.After all of this: the freeze pattern is identical. The bottleneck is upstream of the client, at the API gateway level.
---
Diagnosis
The throttle:
Retry-After.Root cause: a per-account concurrent-stream cap that is decoupled from the plan multiplier and silently buffers (rather than rejects) requests once exceeded.
---
Proposed fixes (ordered by impact)
1. Tie concurrent-stream quota to plan multiplier (highest impact)
The
Nxin Max Nx should grant N× concurrent streams against the Pro baseline, not just N× cumulative tokens. Today the concurrency cap appears constant across tiers, defeating the main reason power users upgrade from 5x to 20x.Implementation sketch: at the rate-limiter (gateway), key the
concurrent_streamsbucket by(account_id, plan_tier)and read the multiplier from the same source as token quotas.2. Surface throttling to the client (low cost, high UX)
Currently throttled sessions hang silently. Return a structured
429withRetry-Afterand a clearerror.type. Claude Code can then show "Rate-limited: queued, retrying in Xs" instead of an indistinguishable spinner.Implementation sketch: the gateway already knows it's throttling; emit the error rather than buffering the request silently.
3. Client-side request queue with exponential backoff
Claude Code (CLI) should treat 429s on streaming requests as queueable, not fatal. A small per-process queue with
(base_delay × 2^attempt) + jitterwould keep sessions alive instead of freezing them, and surface queue position in the UI.4. Burst allowance for session startup
Most freezes happen on the very first request of a new session (typical pattern:
mimir_status/mimir_contextcalls launched all at once). A small burst credit (e.g. allow 4× steady-state RPM for the first 30s of a new session) would absorb the spike when N sessions launch within the same second.5. Session affinity at the LB layer
If the throttle is per-edge-node rather than per-account globally, sticky sessions (
account_id→ consistent backend) would smooth bursts when many sessions launch simultaneously.---
Reproducibility
Reliably triggers the issue in under 60 seconds on Max 20x:
Expected: 4 summaries return.
Actual: 1 returns; 3 hang for the remainder of the 5-hour session window with no error.
---
Why this matters for paying customers
The current behavior makes Max 20x effectively equivalent to Max 5x for parallel-workflow users (the exact cohort paying $200/month). The pricing page implies 20× capacity, but the binding constraint is concurrency — which doesn't scale. Fixing fix #1 alone would resolve most of the open issues listed below.
---
Related issues: #53922, #46037, #38335, #41788, #54750, #8449 — all describe the same throttle from different angles. Consolidating into a single concurrency-quota fix would close most of them.
Happy to provide additional telemetry, network-level captures, or test on a different machine if that helps triage.
Adding an independent confirmation that this is account-scoped concurrency, not per-request-size:
We just hit "Server is temporarily limiting requests · Rate limited" on a cache-warmer ping — a request whose entire payload is on the order of 50 tokens (one read of a small status file, one-token output). No heavy concurrent traffic from our session at the time. We're on Max 20x with multiple Claude Code agents on a single account (which is the relevant load).
That request shape can't trip per-request quotas, weight, or model-specific limits. The only mechanism it can plausibly trip is exactly what @Manuelreyesbravo describes above: an account-scoped concurrent-stream cap saturated by other in-flight requests on the same account, regardless of their individual sizes.
Reinforces the architectural argument: the concurrency cap doesn't scale with plan tier, and the symptom shows up across the entire request size spectrum — from heavy multi-tool sessions down to ~50-token pings.
— Chris
Follow-up with hard data from a 15-minute capture last night.
Inserted a logging tee between our cache-fix proxy and the upstream API to catch the next 429 in full HTTP fidelity. Captured 88 consecutive 429s between 2026-05-08 00:06:33 → 00:21:30 UTC. Several findings that sharpen the picture from your analysis:
x-should-retry: truein the response headers, notRetry-After. CC reads the boolean and infers backoff timing on its own.anthropic-ratelimit-*headers from error responses. Non-error responses includeq5h_utilization,representative_claim, etc. — the 429 strips them. So even sophisticated clients can't see which limit was hit. This is a real observability gap.account-wide concurrent streamdiagnosis. The burst captured here started during a wake-from-idle moment (multiple agents resuming activity within seconds of each other). All requests came from one account, agents on the same host, varying session shapes. Burst occurred at 00:06 UTC — far outside any old-peak schedule. Matches your fix #4 framing precisely.Capture spans 88 × 429 / 1 × control / inter-arrival mean 10.3s / latency p95 882ms (gateway-level rejection, not deep-backend stalls).
— Chris
We hit this exact pattern when stress-testing bulk-spawn workflows. A few observations from outside Anthropic, in case they help while you wait on a CLI-side fix:
claudespawn as a subprocess with its own process group + reading stdout/stderr on a bounded channel makes the failures recoverable: when one session hits the limit, you can detect the error string, back off with jitter, and respawn that single session without nuking the others. Killing the whole batch and restarting is what amplifies the problem.For documentation, +1 on surfacing the burst-vs-sustained limit explicitly. Right now the only way to discover it is by hitting it.
Happy to share more of the harness pattern if useful — we ended up building one for a polling/scheduler service that spawns Claude as a subprocess on an interval and had to solve the same admission-control problem.
@kcarriedo — we ran the same investigation on our end after your comment. Several of your findings check out exactly, and we have a few additions worth folding in.
---
What we verified
1. Process group isolation is not happening today
We confirmed this from a live system with 3 concurrent sessions:
All three claude processes share the same PGID as the Electron parent. There is zero process group isolation between sessions today. Your
setsid()suggestion is architecturally correct — each session should be its own process group leader so signals and resource pressure don't cross-contaminate.2. The "killing the whole batch amplifies the problem" is exactly what we observed
From our
main.log, the failure cascade looked like this:This happened 4 times in 14 minutes. Each reconnect kills any in-flight tool call on the previous connection → session surfaces "Tool result could not be submitted" → user perceives session as broken → restarts the whole batch → new concurrent-init burst → re-triggers the admission throttle. The restart is what amplifies the problem, exactly as you described.
3. The detectable error string in
--output-format stream-jsonThe terminal error string that appears in stdout after retry-budget exhaustion is:
This survives in the stream-json output and is detectable without parsing the full response structure — a simple
incheck on the output line is enough to identify the condition before deciding whether to backoff-and-respawn vs kill the batch.---
What we'd add to your harness pattern
Based on the above, two additions worth considering:
A. Detect the pre-exhaustion signal, not just the terminal error
The retry signal
x-should-retry: trueappears on each of the 6 silent retries before the terminal message. If you're reading stderr through the bounded channel, the intermediate "attempt N/6" state (once #57134 is fixed and CC surfaces it) would let you backoff earlier — before the session actually dies — rather than waiting for the terminal string.B. Stagger + process group isolation together
The 2–5s stagger you mentioned gets sessions through the admission window. Combined with
setsid()isolation, a session that does hit the throttle after staggering can be respawned independently without the SIGTERM propagating to the other sessions via the shared PGID.---
Would you be open to sharing the harness skeleton? Specifically the bounded-channel read + selective respawn logic. We're trying to document a robust reference pattern for the issue tracker that could inform a client-side fix in CC itself — your implementation would be the clearest proof-of-concept available.
— @Manuelreyesbravo
New data point — 500 Internal Server Error triggers the same concurrent-slot starvation as 429
Observed today (2026-05-18) with 5 concurrent sessions on Max 20x / Opus 4.7:
One session received a
500 api_error:From
main.log, the CycleHealth tracker confirmed:The session had already received a first response, then hit the 500 and entered the silent retry loop. It held the concurrent stream slot for the full 107 seconds of retry budget before being marked unhealthy.
During that 107s window, all 4 remaining sessions froze simultaneously — same symptom as the 429 burst, but triggered by a backend 500 instead of the admission throttle.
Implication: the retry-budget exhaustion pattern @cnighswonger documented isn't exclusive to 429s. A 500 occupies the same slot for the same duration before releasing it. If the concurrent-stream cap is the binding constraint, a single 500 is enough to starve all other sessions — even if none of them individually hit a rate limit.
This makes Fix #2 (surface the error with a structured cause) and Fix #7 (show retry state) even more important: today there is no way to distinguish "session is thinking" from "session is burning through retries on a 500" from the user's perspective.
— @Manuelreyesbravo
Third failure mode under concurrent session pressure — spawn failure with 'native binary not found' when binary exists
Observed today (2026-05-19) with 5 concurrent sessions on Max 20x / Opus 4.7:
From
main.log:The binary exists at that exact path (236MB, correct permissions). The session failed to spawn at all —
hadFirstResponse=falseand duration 0s confirm the process never started. This is not a missing binary issue; it's a spawn failure under resource pressure.Context: at the time of failure, 5 concurrent claude sessions were running, each consuming ~380MB RSS (~2GB+ total). The likely cause is
EMFILE(too many open file descriptors) or memory pressure preventingexecvefrom succeeding, with the error surfaced as a generic "binary not found" instead of the actual OS error code.Why this matters for this thread: this is a third distinct failure mode triggered by concurrent session pressure:
main.logAll three produce the same user-visible symptom (frozen or dead session, no actionable error), and all three are invisible without log access. The common thread: Claude Desktop has no mechanism to surface the actual failure cause — OS error, API error code, or retry state — to the user.
— @Manuelreyesbravo
New evidence: "Usage limit reached" displayed when actual quota is 20-30% used
Date: 2026-05-21 ~15:16 local time
Plan: Max (5x)
What happened
Three sessions terminated simultaneously with
reason=api_errorwithin 26 seconds of each other:All three sessions showed "Usage limit reached" in the UI at the moment of termination.
At the same moment, the plan usage dashboard showed:
Why this is different from real quota exhaustion
Real quota exhaustion means all sessions would fail immediately and no new requests would be accepted. What we see instead:
hadFirstResponse=true) — quota was fine when they startedapi_errorhit all three simultaneously — simultaneousapi_erroracross concurrent streams is the signature of a server-side admission throttle, not a per-account quota counterlocal_002c78ba) had been running ~18 minutes on a single request, which matches the known pattern of a request caught in silent retry loops (see #57134) before ultimately failingThe misleading UI message
The string "Usage limit reached" is the same message shown when a user genuinely exhausts their subscription quota. Using it for concurrent stream cap rejection causes users to:
Suggested fix: Distinguish the error message. Options:
Contributing factor: LocalMcpServerManager cascade
In the 4 minutes before the crash,
LocalMcpServerManagerkilled and reconnected the MCP server 4 times due to rapid tab switching (see #58898). Each reconnection triggers areplaceRemoteMcpServerscall that injects new server state into all active sessions, generating additional in-flight API activity. This amplifies the stream count right before the cap is hit.This is a compounding failure: #58898 inflates stream count → cap hit → #53922 terminates sessions → UI shows wrong error → user thinks quota is exhausted.
Summary
| Signal | Value |
|---|---|
| Sessions failed simultaneously | 3 |
| Time window | 26 seconds (15:40–16:06) |
| Current session quota used | ~20% |
| Weekly quota used | ~30% |
| Sessions recovered after process kill+restart | Yes (immediate) |
|
hadFirstResponseon all failed sessions |true|| UI message shown | "Usage limit reached" |
| Actual cause | Concurrent stream cap (
api_error) |The dashboard and the UI are contradicting each other. This erodes trust in the plan usage display and in the reliability of the Max subscription for concurrent workloads.
This is a distinct failure mode from the per-session rate limit errors — it's a burst concurrency cap that fires during the bootstrap phase when multiple sessions handshake simultaneously. The "not your usage limit" language in the error is accurate but unhelpful without knowing what the actual concurrency threshold is.
The retry-with-backoff suggestion here is good. A few things that help in practice while this is being fixed:
Stagger spawning. Rather than bulk-spawning 10 sessions at once, launching them with a 2–3s delay between each avoids hitting the burst threshold. Not elegant, but nearly eliminates the failure on most runs.
Separate the bootstrap from the work. The API handshake on startup is the expensive part. If your script waits for session N to report "ready" before spawning N+1, concurrency spikes are avoided entirely — though this adds total spawn time.
Explicit error surface. The bigger issue is that the error gives no signal on retry timing or current concurrency slot availability. Even a Retry-After header in the response would make client-side backoff well-calibrated rather than guessed.
Related: #62426 hits this from the sustained multi-session angle rather than burst-at-reset, suggesting the concurrency throttle applies at both burst and steady-state.
Closing for now — inactive for too long. Please open a new issue if this is still relevant.