Skip to content

fix(cli): stop run from sleeping through an exhausted quota - #42340

Open
DaisukeYoda wants to merge 1 commit into
anomalyco:devfrom
DaisukeYoda:fix/run-exit-on-quota-limit
Open

fix(cli): stop run from sleeping through an exhausted quota#42340
DaisukeYoda wants to merge 1 commit into
anomalyco:devfrom
DaisukeYoda:fix/run-exit-on-quota-limit

Conversation

@DaisukeYoda

Copy link
Copy Markdown

Issue for this PR

Closes #40747

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

opencode run produces no output and never returns when a model's quota is exhausted.

It isn't wedged, it's sleeping. The quota error is classified as retryable, and delay() honors the provider's retry-after, which for a monthly limit is days. While the schedule sleeps halt() never runs, so no session.error and no idle are published, and loop() in cli/cmd/run.ts breaks only on idle and has no branch for retry status. The TUI is unaffected because it shows the retry in its footer; a headless run has nowhere to show it.

I left the retry timing alone — honoring long retry-after values was deliberate in 0a2d7af, and waiting out a short rate limit is useful. Only the non-interactive loop changes:

  • print the retry status, which today prints nothing at all in headless runs
  • exit non-zero when action.reason is free_tier_limit / account_rate_limit; transient retries (429/5xx) carry no action.reason and still wait and retry as before
  • abort the session before returning, because breaking out of the loop isn't enough to end the process — the sleeping schedule holds the in-process server open, and without the abort the CLI has decided to stop but still has to be killed

How did you verify your code works?

New test in test/cli/run/run-process.test.ts: the test provider returns a 429 carrying GoUsageLimitError with retry-after: 345600, and the run must exit non-zero and mention the limit on stderr. On unmodified dev it hangs until the harness kills it at 30 s; with this change it passes in ~2 s.

Setting that header needed an optional headers argument on httpError() / llm.error() in the test harness — retry-after drives the delay and there was no way to set it before.

bun test test/session/retry.test.ts test/cli/run/ → 250 pass, 5 skip, 0 fail.
tsc --noEmit → matches the dev baseline (12 pre-existing errors in packages/tui).

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Quota errors are retryable, and the retry schedule honors the provider's
retry-after, which for a monthly limit is days. While it sleeps, halt()
never runs, so no session.error and no idle are published. The
non-interactive loop breaks only on idle and ignores retry status, so it
waits with no output and no exit.

Handle retry status in that loop: print it, and exit non-zero when
action.reason marks the limit as non-transient. Transient retries carry
no action.reason and still wait and retry as before. The session is
aborted first because the sleeping schedule otherwise keeps the process
alive after the loop breaks.

Retry timing is unchanged; honoring long retry-after values is
deliberate (0a2d7af).

Fixes anomalyco#40747
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opencode run hangs indefinitely when the usage quota is exhausted, instead of reporting the error it already has

1 participant