[BUG] Rate-limit / usage-limit is emitted as a synthetic assistant turn with terminal stop_reason=stop_sequence, indistinguishable from a clean completion

Open 💬 0 comments Opened Jun 16, 2026 by TweedBeetle

What happens

When a session hits a rate limit, Claude Code records it in the session JSONL as a synthetic assistant message that carries a terminal stop_reason of stop_sequence. This applies to both the subscription usage limit ("You've hit your session/weekly limit · resets X") and the server-side throttle ("Server is temporarily limiting requests (not your usage limit)").

Real entry (Max 20x, weekly-limit case):

{
  "type": "assistant",
  "isApiErrorMessage": true,
  "error": "rate_limit",
  "apiErrorStatus": 429,
  "message": {
    "role": "assistant",
    "model": "<synthetic>",
    "stop_reason": "stop_sequence",
    "stop_sequence": "",
    "content": [
      { "type": "text", "text": "You've hit your weekly limit · resets Jun 3 at 4pm (Europe/Berlin)" }
    ]
  }
}

The stop_reason is stop_sequence, which is the same terminal stop a normal finished turn produces. There is no distinct stop reason for "this turn was cut off by a rate limit." The only signals that this is a rate limit are the top-level isApiErrorMessage, error, and apiErrorStatus fields, which sit outside the message object and are not reflected in stop_reason.

Why it's a problem

Any consumer that reads the session to decide whether work is finished (orchestration layers, headless or autonomous runs, session monitors, completion hooks) sees a terminal stop_reason and treats the session as cleanly done. A session that was actually parked at a rate limit, mid-task, gets treated as complete. In an orchestration setup that means in-flight work is torn down, "on done" steps fire on unfinished output, and workers get reaped instead of resumed after the reset.

This is most acute on the multi-agent and Workflow features that Claude Code ships and promotes, because those run many concurrent sessions and rely on reading stop state to know when each one is done.

How often this fires (one heavy user's data)

I went through my own session logs on Max 20x. Since late March there have been 752 of these rate-limit entries across my sessions, deduped by entry uuid:

  • About 350 are the server-side "(not your usage limit)" throttles, arriving in bursts (193 in a single day, on two separate days in June).
  • About 400 are actual usage-limit blocks (roughly 44 distinct episodes), and the rate has climbed from 22 in March to 240 so far in June.

The cluster of issues whose titles are just the raw error text (#68803, #68765, #68162, #67924, #67693, #67592, and others) suggests this fires constantly across the user base, currently with no clean way for tooling to tell it apart from a normal completion.

What I'd like

  1. Give rate-limit and usage-limit a distinct, machine-detectable stop signal, rather than reusing the terminal stop_reason: stop_sequence. A dedicated stop_reason value (or a non-terminal marker) that a consumer can detect would let tooling pause and resume instead of mistaking the session for finished.
  2. Ideally, handle it gracefully in the session itself: pause at the limit and resume after the reset rather than ending the turn. This overlaps with #41502 (--wait-on-usage-limit for non-interactive sessions).

Related

  • #66268 ([BUG] Usage limit reached unexpectedly after 429 rate limit errors). Possibly the same area. Worth checking whether bursts of the server-side "(not your usage limit)" 429s contribute to hitting the actual usage limit.
  • #41502 (Feature request: --wait-on-usage-limit flag for non-interactive sessions).
  • #60674 (Feature request: expose account usage limits in the CLI).

Environment

  • Claude Code version: 2.1.178
  • Plan: Max 20x
  • OS: macOS

View original on GitHub ↗