Skip to content

fix(server): Claude wake turns keep the rejected limit's reset time - #13539

Merged
shivamhwp merged 3 commits into
pingdotgg:t3code/codex-turn-mappingfrom
saphid:fix/claude-wake-rate-limit-reset
Sep 27, 2026
Merged

shivamhwp merged 3 commits into
pingdotgg:t3code/codex-turn-mappingfrom
saphid:fix/claude-wake-rate-limit-reset

Conversation

@saphid

@saphid saphid commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

When Claude hits a usage limit during a background-task wake, the limited-thread card says "The provider did not report a reset time", even though the message right above it reads "You've hit your session limit · resets 11:10am (Australia/Sydney)". Because resetAt is null, Resume at reset and Snooze until reset are also unavailable.

The provider did report it. In a real incident, the provider log shows rate_limit_event {status: "rejected", rateLimitType: "five_hour", resetsAt: 1790298600} (11:10 AEST) arriving 65 ms before the 429 result. It arrived while the CLI was starting its notification wake, before the continuation turn existed. The rate_limit_event branch of handleSdkMessageFrame runs ahead of the no-active-turn wake-buffer branch and always returns, so the adapter dropped the frame. The continuation then replayed only the buffered assistant/result frames and finalized with failure.resetAt: null.

Fix

  • While no turn is active, park rate_limit_event frames in the wake buffer, and count them as wake evidence. The existing offer gate still keeps them from requesting a continuation by themselves. The continuation drain replays them onto the new turn, which records the reset time and emits the usage-limit pause notice.
  • Remove the context !== null guards that became dead after the early return.

Tests

A new regression test in ClaudeAdapterV2.test.ts replays the incident's frame order: notification, rejected event, rate-limit assistant, 429 task-notification result, then the continuation turn. It asserts:

  • failure.resetAt === "2026-09-25T01:10:00.000Z" (fails with null before the fix)
  • the pause notice is emitted
  • the parked frame requests no continuation on its own

vp test run apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts: 119 passed on 87c67bd5a1. With the adapter change reverted, the new test fails with resetAt: null, so the bug is present on the current base. Server tsc --noEmit is clean, and lint/fmt are clean on the touched files.

Known trade-off

Idle rate-limit frames are parked unconditionally. A stray one that never gets a wake result stays until the next continuation on that native thread and replays there once. The next drain clears it, and a fresh event for the same window overwrites it. Gating parking on existing wake evidence would make the fix depend on the order of the wake's frames, which is the kind of dependency that caused this bug.

Implemented with GLM 5.3 Flash in Claude Code, and independently reviewed by SWE-2 Max (devin -p --model swe-2-max, no blockers). The PR was prepared with Claude Opus 5.5 in Claude Code.

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Sep 25, 2026
@macroscopeapp

macroscopeapp Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

@macroscopeapp

macroscopeapp Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a0eaca5

Macroscope's review found this PR approvable — This is a narrowly scoped Claude wake-turn bug fix that preserves the provider’s reported reset time without allowing rate-limit frames to trigger continuations on their own. The production change is isolated and covered by a focused regression test.

You can add or adjust custom eligibility rules. Learn more.

@saphid
saphid force-pushed the fix/claude-wake-rate-limit-reset branch from ae1d5be to d458d92 Compare September 25, 2026 02:14
@github-actions github-actions Bot added size:S 10-29 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Sep 25, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Sep 25, 2026
@juliusmarminge
juliusmarminge force-pushed the t3code/codex-turn-mapping branch from fe4f6ad to 87c67bd Compare September 25, 2026 05:55
A background-task wake can begin with a rejected rate_limit_event: the
CLI reports the limit before the continuation turn exists, the adapter
dropped the frame, and the turn then failed with failure.resetAt null —
so clients showed "the provider did not report a reset time" right
under a message naming one. A real incident had resetsAt on the wire
65ms before the failure.

Park rate_limit_event frames in the wake buffer while no turn is active
and count them as wake evidence that never offers a continuation; the
drain replays them onto the continuation turn, which records the reset
time and announces the pause. The null-context guards in the branch
became dead after the early return and are folded away.

The regression test drives the incident's frame order end to end and
asserts failure.resetAt plus the pause notice.

Implemented by GLM (glm-5.3-flash) in Claude Code; independently
reviewed by SWE-2 Max (devin -p --model swe-2-max), no blockers.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@saphid
saphid force-pushed the fix/claude-wake-rate-limit-reset branch from d458d92 to 4763a7d Compare September 25, 2026 06:32
@macroscopeapp
macroscopeapp Bot dismissed their stale review September 27, 2026 08:50

Dismissing prior approval to re-evaluate a0eaca5

@shivamhwp
shivamhwp merged commit 0dcb029 into pingdotgg:t3code/codex-turn-mapping Sep 27, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants