[BUG] Rate-limit dialog blocks the session with a single option whose only effect is dismissal

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 3, 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?

When a usage limit is hit, the rate-limit-options dialog opens. On my account it renders with exactly one option, and selecting that option does nothing that Esc doesn't already do — so the session blocks on a keypress to resolve a choice that has no branches.

Reading the shipped bundle for 2.1.220 (identifiers below are mangled by minification), the cancel entry and the dialog's onCancel are the same handler:

X5t = function(){ M("tengu_rate_limit_options_menu_cancel",{});
                  W9e(undefined, {display:"skip"}) }

Oni = function(v){ /* … */ else if (v === "cancel") X5t() }

// dialog: { title: "What do you want to do?", onCancel: X5t, … }

Enter on option 1 and Esc both call W9e(undefined, {display:"skip"}).

This is the degenerate case of the menu. The other entries are appended only when the server's upgradePaths includes them:

if (paths.includes("overage"))      opts.push({ value: "extra-usage", … })
if (paths.includes("upgrade_plan")) opts.push({ value: "upgrade", … })

const cancel = { value: "cancel",
                 label: billingType === "usage_based"
                          ? "Stop"
                          : "Stop and wait for limit to reset" }

When upgradePaths includes neither, only cancel survives — and the dialog opens anyway, with nothing in it to decide.

Why this is more than cosmetic. The dialog requires a keypress, so an unattended session halts there. I run long-lived sessions driven by recurring scheduled prompts; ordinary transient errors print to the console and the next tick retries, but this modal stops the session until a human is present — to answer a question that has one answer, which does nothing.

What Should Happen?

When the option list reduces to only the cancel entry, don't open a modal. Print the notice (limit reached, resets at <time>) and let the session continue without input.

A dialog should block only when there is a decision to make.

Error Messages/Logs

You've hit your limit · resets <time>

  What do you want to do?
❯ 1. Stop and wait for limit to reset

  Enter to confirm · Esc to cancel

Steps to Reproduce

  1. Sign in with an OAuth subscription account (not usage-based billing) whose server response returns neither overage nor upgrade_plan in upgradePaths.
  2. Run an interactive session until the "usage limit" is hit.
  3. The dialog opens with a single option, and the session blocks until a key is pressed.
  4. Press Enter on option 1, then repeat and press Esc instead. Both produce the same result — the dialog dismisses and nothing else changes.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.220

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

Not a duplicate of:

  • #13354 / #35744 (auto-continue after the limit resets) — I am not asking for auto-continue or auto-resume. Only: do not open a modal that has no actionable option.
  • #40898 (closed NOT_PLANNED) — that request concerned reordering and adding options on a three-option menu. This is the zero-actionable-option case, which it did not cover.

A suppression carve-out already exists, but users cannot reach it. The dispatch is guarded by:

if (AN.current) return false;      // once-per-session latch
if (LR() || rs()) return false;    // repl bridge active, or background session

function rs(){ return Got() === "bg" }
function Got(){ const e = env.CLAUDE_CODE_SESSION_KIND;
                if (e === "bg" || e === "daemon" || e === "daemon-worker") return e; }

Background sessions are already exempt. But CLAUDE_CODE_SESSION_KIND is an internal role marker, always set as part of a bundle (CLAUDE_BG_BACKEND, CLAUDE_JOB_DIR, CLAUDE_BG_AUTH_SNAPSHOT_PATH), so setting it by hand on a foreground session to dodge the dialog means running in a half-configured background role. There is no user-facing equivalent.

Adjacent observation, not the bug being filed: Got() recognizes daemon and daemon-worker, but rs() compares strictly to "bg", so daemon-worker sessions are not exempt. That reads unintentional.

Context on the open path: the v2.1.90 changelog records a fix for "an infinite loop where the rate-limit options dialog would repeatedly auto-open", which appears to be why the AN.current once-per-session latch exists — so this dialog's open path has been revised before.

View original on GitHub ↗