[BUG] Synthetic rate_limit error returned with 0 input/output tokens on main agent despite low usage and extended idle periods

Resolved 💬 3 comments Opened Mar 29, 2026 by rwp65 Closed Apr 1, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

  • Claude Code v2.1.86, Opus 4.6
  • model=<synthetic> with service_tier=None — never reaches the real API
  • input_tokens=0, output_tokens=0 — zero processing
  • Occurs on agent=main, not just subagents
  • 21% plan usage at time of error
  • Occurs after gaps of 25 min, 54 min, and even 11 hours
  • Main conversation works seconds later (same session, same context)
  • cache_read_input_tokens: 918,977 on successful calls — large context may be relevant

What Should Happen?

When a user sends a message at 21% plan usage with no concurrent requests and hours of idle time, the API should process the request normally — not
return a synthetic rate_limit error with 0 tokens. Specifically:

  1. The request should reach the actual API (not be intercepted as model=<synthetic>)
  2. A service_tier should be assigned (not None)
  3. Tokens should be consumed and a response generated
  4. If a legitimate rate limit exists, the error should include: which limit was hit, when it resets, and current usage — not a generic "Rate limit

reached" with zero diagnostic information

  1. An 11-hour idle period should be more than sufficient to reset any per-minute or per-hour throughput limit

Error Messages/Logs

# Claude Code Rate Limit Bug — Full Log Evidence
# Version: Claude Code 2.1.86
# Model: claude-opus-4-6 (1M context)
# Plan usage at time of errors: 21%
# Platform: Linux 6.17.0-19-generic (Ubuntu)

## RATE LIMIT ERRORS (all from session JSONL log)
## Format: timestamp | agent | model | input_tokens | output_tokens | cache_read | cache_create | service_tier | error

# === March 25 ===
2026-03-25T14:27:03.556Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | "You've hit your limit · resets 12pm (America/Chicago)"

# === March 27 ===
2026-03-27T15:08:37.493Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | "You've hit your limit · resets 11am (America/Chicago)"

# === March 28 (8 errors, gaps of 20-90 minutes between attempts) ===
2026-03-28T12:17:17.013Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | "API Error: Rate limit reached"
2026-03-28T12:27:31.672Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | "API Error: Rate limit reached"
2026-03-28T14:52:06.576Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (147 min gap)
2026-03-28T14:52:12.024Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-28T15:23:45.457Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (31 min gap)
2026-03-28T15:28:52.507Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-28T15:48:07.080Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (20 min gap)
2026-03-28T16:14:31.230Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (26 min gap)

# === March 29 (12 errors, including an 11-HOUR gap that still gets rejected) ===
2026-03-29T00:56:35.936Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (9 HOUR gap from last error)
2026-03-29T00:56:40.041Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T00:57:50.254Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T03:51:50.608Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (3 HOUR gap)
2026-03-29T03:51:55.065Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T04:46:07.221Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (54 min gap)
2026-03-29T04:46:16.160Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T04:46:48.965Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T04:50:31.034Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T04:51:42.804Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit
2026-03-29T05:03:01.889Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit | (12 min gap)
2026-03-29T05:03:35.017Z | agent=main | model=<synthetic> | in=0 out=0 | cache_r=0 cache_c=0 | tier=None | err=rate_limit


## SUCCESSFUL CALLS (same session, seconds after rate limit errors)
## Proves the main conversation works fine — the rate limit is spurious

2026-03-29T05:08:24.367Z | agent=main | model=claude-opus-4-6 | in=1 out=225 | cache_r=918977 cache_c=375 | tier=standard | SUCCESS
2026-03-29T05:08:44.373Z | agent=main | model=claude-opus-4-6 | in=1 out=301 | cache_r=920004 cache_c=584 | tier=standard | SUCCESS
2026-03-29T05:08:55.261Z | agent=main | model=claude-opus-4-6 | in=1 out=266 | cache_r=920588 cache_c=1250 | tier=standard | SUCCESS


## KEY OBSERVATIONS
# 1. ALL rate limit errors show model=<synthetic> — request never reaches real API
# 2. ALL show service_tier=None — no tier assigned
# 3. ALL show input_tokens=0, output_tokens=0 — zero processing
# 4. ALL are on agent=main — not subagent/background work
# 5. Gaps between errors: 20min, 31min, 54min, 3hrs, 9hrs, 11hrs — still rejected
# 6. Plan usage dashboard shows 21% at time of errors
# 7. Successful calls (same session) show cache_read=918K+ tokens — large context
# 8. No concurrent requests running during most failures
# 9. Affects both direct user messages AND subagent spawns equally


## RAW JSONL SAMPLE (one rate limit + one success for comparison)

# Rate limit (synthetic, 0 tokens, no tier):
{"timestamp":"2026-03-29T04:46:07.221Z","message":{"id":"54dd0bfd-c56e-4634-b09a-c45cc8351e5a","container":null,"model":"<synthetic>","role":"assistant","stop_reason":"stop_sequence","stop_sequence":"","type":"message","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":null,"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":null,"iterations":null,"speed":null},"content":[{"type":"text","text":"API Error: Rate limit reached"}],"context_management":null},"error":"rate_limit"}

# Success (real model, real tokens, real tier — 22 minutes later, same session):
{"timestamp":"2026-03-29T05:08:24.367Z","message":{"model":"claude-opus-4-6","id":"msg_...","type":"message","role":"assistant","usage":{"input_tokens":1,"cache_creation_input_tokens":375,"cache_read_input_tokens":918977,"output_tokens":225,"service_tier":"standard"},"content":[{"type":"tool_use",...}]}}


## ENVIRONMENT
# Claude Code version: 2.1.86
# Model: claude-opus-4-6 (Opus 4.6 with 1M context)
# OS: Linux 6.17.0-19-generic (Ubuntu)
# Session context: ~920K cached tokens (large codebase, long session)
# Plan: Max (showing 21% usage)
# Settings: {"env":{"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS":"1"},"effortLevel":"high"}

Steps to Reproduce

  1. Open Claude Code v2.1.86 on the Max plan at ~21% usage
  2. Work in a large codebase session until context cache reaches ~900K+ tokens
  3. Spawn a subagent using the Agent tool (any task)
  4. Observe: subagent returns API Error: Rate limit reached with 0 tokens processed
  5. Wait 20+ minutes, try again — same error
  6. Wait 11+ hours, try again — same error
  7. Send a direct message (not subagent) — it works fine with model=claude-opus-4-6, service_tier=standard, and 918K+ cache_read tokens
  8. Try spawning another subagent — rate limit error again with model=<synthetic>, 0 tokens

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.86

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Xterm

Additional Information

  • This session has been running for ~6 days continuously (started 2026-03-23), producing 950+ file changes, 200+ database migrations, and spawning 100+

successful subagents before the rate limiting began

  • The first 4 days of the session had zero rate limit issues with heavy parallel agent usage (up to 4 concurrent subagents)
  • Rate limit errors began appearing on day 5 (March 28) and became persistent on day 6 (March 29)
  • The session JSONL file is 13.5MB — the growing session context may be a factor
  • cache_read_input_tokens on successful calls is 918,977 — near the 1M context ceiling
  • The <synthetic> model tag suggests Claude Code is generating the error locally rather than receiving a 429 from the API — unclear if the request ever

leaves the client

  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 is enabled — this may affect how agent token budgets are allocated
  • effortLevel: "high" is set in settings.json
  • Restarting Claude Code (/exit and relaunch) temporarily resolves the issue for a few interactions, then it returns — suggesting client-side state

accumulation

Questions for the team:

  1. Does model=<synthetic> mean the rate limit is enforced client-side before the API call, or is the API returning this?
  2. Is there a separate token budget for subagents vs the main conversation?
  3. Does cache_read_input_tokens near 920K count toward rate limits differently than fresh input tokens?
  4. Is there a per-session or per-day ceiling that isn't reflected in the usage dashboard?

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗