Skip to content

fix(opencode): show usage-limit warnings while OpenCode 2 retries - #28375

Open
only21mil wants to merge 3 commits into
pingdotgg:mainfrom
only21mil:fix/audit-m040
Open

only21mil wants to merge 3 commits into
pingdotgg:mainfrom
only21mil:fix/audit-m040

Conversation

@only21mil

@only21mil only21mil commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When OpenCode 2 hits a rate limit or a usage window (HTTP 429), it schedules a retry, and T3 shows a retry row while it waits. That row is always classed as a provider error. If the retries run out, the final row for the same error is classed as a usage limit. So while the retry runs, web shows the row without the usage-limit warning, and mobile shows it with the provider-error alert icon. It only gets the usage-limit look once the turn has failed.

updateRetry in the OpenCode 2 adapter hard-codes class: "provider_error". The terminal session.execution.failed case already classes provider.rate-limit and non-402 provider.quota errors as usage_limit.

Change

  • One small helper, isUsageLimit(type, status), holds the existing terminal rule: a 429 quota or a rate limit is a usage limit, and a 402 quota (an empty balance) is not.
  • updateRetry and the terminal case both use it. The retry row stays retryable: true.

Claude retry rows already do this: a 429 retry is usage_limit. Server-side usage-limit handling (recovery, auto-resume, usage refresh, the thread's last error) reads only failed rows, so a running retry row with this class changes only how the row looks. If the retry succeeds, the row turns completed and shows as recovered, as before.

Scope and approval

No issue or discussion. This is a very small, focused fix for an obvious bug: the same error gets two classes depending only on whether OpenCode will retry it, and web and mobile already have a usage-limit look for a running retry row. OpenCode v1 has no retry rows.

Verification

  • apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts:
    • The existing "classifies $name as $failureClass" table now schedules a retry before each failure and checks the running retry row's class.
    • The retry block's shared fixture for a 429 rate limit now expects usage_limit.
    • On main, 8 tests fail: the three 429 rows and the five retry-block tests. The 402 row passes on main and still passes, so an empty balance stays a provider error.
    • With the fix, all 84 tests in the file pass.
  • vp run check:changed: format, lint, knip, typecheck and the changed tests pass.
  • Real clients against an isolated server, with OpenCode 2.0.18 pointed at a local stub that answers HTTP 429 and Retry-After: 600, so the real adapter produces a running retry. Before uses main's adapter file; after uses this branch. The stored row reads failure.class: provider_error on main and usage_limit with this PR.

Web, main: the running retry row has no warning.

Web on main: running retry row without a warning

Web, this PR: the same row shows the usage-limit warning.

Web with this PR: running retry row with the usage-limit warning

Desktop, main: the running retry row has no warning.

Desktop on main: running retry row without a warning

Desktop, this PR: the same row shows the usage-limit warning.

Desktop with this PR: running retry row with the usage-limit warning

iOS, main: the rate-limit retry row has the provider-error alert icon.

iOS on main: retry row with the alert icon

iOS, this PR: the row has the icon mobile uses for usage limits.

iOS with this PR: retry row with the usage-limit icon

Android, main: the rate-limit retry row has the provider-error alert icon.

Android on main: retry row with the alert icon

Android, this PR: the row has the icon mobile uses for usage limits.

Android with this PR: retry row with the usage-limit icon

Not checked:

  • macOS and Windows desktop builds. Desktop was captured from a Linux build.
  • A real provider's 429. The stub returns a fixed rate-limit error, which OpenCode classes as provider.rate-limit.
  • Mobile draws usage-limit rows with an X (its existing glyph for warnings). This PR doesn't change that mapping. The Android emulator renders in software, so its screenshots show the icon, not layout.

Prior PRs

#18315 by Marker-bit made OpenCode Go quota stops show as usage limits on the final row. This PR applies the same rule to the retry row that comes before it.

Generated with Claude Opus 5.5 and GPT-6.1 Terra in T3 Code Nightly

A retry scheduled for a rate limit or a 429 quota showed as a provider
error while it ran, and only the terminal failure row was classed as a
usage limit. Retry rows now share the terminal row's classification, so
an out-of-funds 402 quota stays a provider error and the row stays
retryable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 31, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at d181dbd

Macroscope's review found this PR approvable — OpenCode 2 quota and rate-limit retries now appear as usage-limit warnings using the existing classification rules. Retry behavior and terminal error classification remain unchanged.

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

@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between d181dbd and 278a992.

Files selected for processing: 2
  • apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts
  • packages/provider-opencode/src/server/v2/adapter.ts

Limit details: You’ve used all 10 included reviews currently available.

Walkthrough

The OpenCode 2 adapter now uses a shared rule to classify rate-limit and quota errors as usage limits. Tests check the classification of the retry event and terminal failure.

Changes: OpenCode 2 usage-limit classification

Layer / File(s) Summary
Classify retry and execution failures
packages/provider-opencode/src/server/v2/adapter.ts
A shared classifier treats provider.rate-limit errors and provider.quota errors with statuses other than 402 as usage limits. Retry and execution failure handling use this classifier.
Verify retry and terminal events
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts
The test checks the running retry error row, including its retryability and failure class. It also checks the retry and terminal failure classifications.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Possibly related issues

  • pingdotgg/t3code#29289: This change classifies rate-limit and quota errors as usage limits, but does not cover provider.unknown messages or resetAt extraction.
  • pingdotgg/t3code#28936: This change applies usage-limit classification to OpenCode 2 retries; the Codex retry behavior remains outside its scope.
  • pingdotgg/t3code#24885: This change applies the classification to OpenCode 2, but does not cover the Codex capacity case.
  • pingdotgg/t3code#27096: This change classifies OpenCode 2 quota and rate-limit errors, but does not cover the Muse tier-limit case.

Suggested reviewers: fixfon

Merge Risk: Minimal · up to 278a9

The change is mergeable; it updates retry-row presentation without altering recovery or thread error handling.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: showing usage-limit warnings while OpenCode 2 retries.
Description check Passed The description includes complete Problem, Change, Scope and approval, and Verification sections. It explains the behavior change, implementation, scope rationale, test results, manual verification, a…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes complete Problem, Change, Scope and approval, and Verification sections. It explains the behavior change, implementation, scope rationale, test results, manual verification, and limitations.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

@github-actions github-actions Bot added size:S 10-29 changed lines (additions + deletions). and removed size:XS 0-9 changed lines (additions + deletions). labels Nov 2, 2026
@maria-rcks

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Merged main without rebasing and resolved the OpenCode adapter conflict, preserving main's Claude limit classification and reset timestamp handling. Adjusted the retry assertion for main's provider.unknown cases so their existing provider-error retry behavior stays unchanged. The original rate-limit/quota retry fix remains intact, including 402 handling and retry recovery.

At 278a992f209a389fc7abccad391bfd5a9d731329, Blacksmith passes all 88 OpenCode2 adapter tests and vp run check:changed github/main: format, lint, knip, GraphQL, 10 package typechecks, and 89 tests. Two independent scoped reviews approve this head. The diff remains two files. Original client evidence is retained; real provider/client interaction was not repeated during this merge fix.

@maria-rcks

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

partial verification at 278a992f209a389fc7abccad391bfd5a9d731329 on a linux boat vm. ran github/main (e1e520c93c) in a separate worktree first, stopped it, then ran the exact pr head. installed dependencies on both. used isolated dev state, playwright from repo dependencies, and /usr/bin/google-chrome. created the project and threads through the web app. no mocks, fixtures, or synthetic provider errors.

  • real codex gpt-6.1-sol turns completed on both revisions, reading package.json and reporting its package name and package manager.
  • real opencode 2.0.28 with opencode/beam-free returned a terminal free-usage quota error on both revisions. the persisted error row was status: failed, class: usage_limit, code: provider.quota, retryable: null. the web client showed the usage warning.
  • on the pr, switching that failed opencode thread to codex gpt-6.1-sol produced a context handoff and a successful response. the usage-limit banner cleared.
  • fresh blacksmith run: vp test run apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts, 88/88 passed. no source changes or pushes.

main, terminal quota warning:

main: real opencode terminal quota warning

pr, same prompt and provider, terminal quota warning:

pr: real opencode terminal quota warning

pr interaction: real terminal quota failure, model switch, context handoff, and successful codex response. a 15-second idle gap was trimmed; this is not retry-recovery evidence.

pr: terminal quota failure followed by successful codex handoff

runtime transcript

Not verified

the actual changed behavior remains unverified. the real service failed immediately and never scheduled a retry on either revision. there were zero running retry rows, so i could not reproduce the original running-retry bug on main or prove its fix on the pr. i did not replace the provider with a stub or manufacture a 429. passing adapter tests and terminal-warning captures do not prove the running retry path.

also unverified: real 402 balance errors, automatic retry recovery, native ios/android clients, and native desktop builds. these require an appropriate real provider condition or native environment. this is a partial runtime check, not a ready-to-merge claim.

This branch has not been deployed

No deployments
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:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants