You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(server): OpenCode ChatGPT usage limits keep their reset time - #26264
ChatGPT already says when the limit lifts. Its 429 body is:
{"error":{"type":"usage_limit_reached","message":"The usage limit has been reached","plan_type":"plus","resets_at":1788753614,"resets_in_seconds":57361}}
OpenCode 2 forwards that body on session.execution.failed as error.response.body, but the adapter ignored it.
Change
When the OpenCode 2 adapter classifies a failure as a usage limit, it decodes error.resets_at (Unix seconds) from response.body and passes it as resetAt to makeProviderFailure, which already validates it and keeps it only for usage_limit. Claude limits keep the message-based reset from https://git.cafe/pingdotgg/t3code/pulls/29301, which takes precedence. Other bodies without that field (OpenCode Go, Zen, other providers) still produce no reset time, as before. No contract, client, or orchestration changes; the existing reset UI and auto-resume worker pick it up.
Added a ChatGPT 429 row to the existing classifies $name as $failureClass table in OpenCode2AdapterV2.test.ts, expecting resetAt: "2026-10-27T15:25:41.000Z". With the adapter change stashed it fails on exactly that row (missing resetAt); the other rows pass. With the change, the file passes (89/89 after merging current main).
vp run check:changed: format, lint, knip, typecheck (36 packages), and the changed test file all pass.
Payload shape: taken from a real ChatGPT Plus usage-limit 429 recorded in my local OpenCode store (responseBody on an OpenCode 1.x message). I confirmed that OpenCode 2.1.3's execution-failure mapper includes response: { body } when the HTTP error has a body, and the @opencode/schema 2.0.28 Session.StructuredError used here declares response.body.
Not checked: a live end-to-end ChatGPT quota refusal through T3. My quota had already reset when I wrote this, and I couldn't get OpenCode 2 to route to a local mock 429 endpoint. Server-only change, so there are no screenshots.
Implementation, tests, and PR: Claude Opus 5.5 via Claude Code in T3 Code.
ChatGPT-backed OpenAI models answer usage limits with a 429 whose body
carries error.resets_at. Read it from the failure's response body so the
usage-limit failure gets a resetAt, enabling resume and snooze at reset.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Macroscope's review found this PR approvable — Preserves provider-reported usage-limit reset times for existing display and recovery flows. Invalid timestamps are ignored, and automatic recovery remains controlled by existing preferences, disabled by default.
You can add or adjust custom eligibility rules. Learn more.
It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.
No actionable comments were generated in the recent review.
Walkthrough
The OpenCode 2 adapter parses a reset timestamp from a usage-limit response body and uses it when no Claude-specific reset time is available. A test covers a ChatGPT quota error and checks the expected reset time.
Changes: OpenCode 2 reset-time handling
Layer / File(s)
Summary
Parse and attach reset time packages/provider-opencode/src/server/v2/adapter.ts, apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts
The adapter converts a finite Unix-seconds resets_at value to an ISO timestamp. It returns null if decoding or date conversion fails, and uses the parsed timestamp when a Claude-specific reset time is unavailable. The test checks the ChatGPT quota-error case and conditionally expects resetAt.
OpenCode ChatGPT usage-limit failures now keep their reset time, so the reset UI and auto-resume can use it. Other failures behave as before. No merge-blocking risk was found.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: OpenCode ChatGPT usage-limit failures now keep their reset time, which matches the adapter change that reads error.resets_at from the response body and passes it as re…
Description check
The description follows the template's sections (Problem, Change, Scope, Verification) and fills each one with specific content. It explains the failure, the 429 payload shape, the adapter fix, and th…
Linked Issues check
Issue #26265 requires OpenCode 2 ChatGPT usage-limit failures to carry the reset time from the 429 body. The whole-PR diff (merge base to reviewed head 7500e4b) in `packages/provider-opencode/src/ser…
Out of Scope Changes check
The whole-PR diff changes two files: packages/provider-opencode/src/server/v2/adapter.ts (the usageLimitResetAt helper and the resetAt fallback in the usage-limit branch) and `apps/server/src/or…
Full details: Title check
Explanation
The title names the main change: OpenCode ChatGPT usage-limit failures now keep their reset time, which matches the adapter change that reads error.resets_at from the response body and passes it as resetAt. It follows conventional commit format and is specific.
Full details: Description check
Explanation
The description follows the template's sections (Problem, Change, Scope, Verification) and fills each one with specific content. It explains the failure, the 429 payload shape, the adapter fix, and the precedence with the Claude reset path. It links the issue and the prior work. It gives focused test results and states what was not checked. The template's agent attribution line is present, and the description notes that the change is server-only, so no screenshots are needed. The description is mostly complete and clear.
Full details: Linked Issues check
Explanation
Issue #26265 requires OpenCode 2 ChatGPT usage-limit failures to carry the reset time from the 429 body. The whole-PR diff (merge base to reviewed head 7500e4b) in packages/provider-opencode/src/server/v2/adapter.ts adds decodeUsageLimitBody, which decodes error.resets_at (Finite, Unix seconds) from response.body with Schema.decodeUnknownOption. usageLimitResetAt converts it to ISO with DateTime.make and DateTime.formatIso, and returns null when the body or field is absent or invalid. The usage-limit failure now sets resetAt: claudeLimit?.resetAt ?? usageLimitResetAt(response?.body), so the Claude message-based reset keeps precedence. The previous review also confirmed a ChatGPT provider.quota 429 row in OpenCode2AdapterV2.test.ts expecting resetAt: "2026-10-27T15:25:41.000Z", which matches 1793114741 Unix seconds. The head changed through a merge of main. The current adapter diff matches the earlier reviewed logic. The test diff was not re-read at this head. The UI and auto-resume paths were not re-verified in this review; the PR states they are unchanged and read the field.
Full details: Out of Scope Changes check
Explanation
The whole-PR diff changes two files: packages/provider-opencode/src/server/v2/adapter.ts (the usageLimitResetAt helper and the resetAt fallback in the usage-limit branch) and apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts (one ChatGPT row, and a runner that passes resetAt only when a row defines it). All changes serve issue #26265. No contract, client, orchestration, or unrelated provider changes appear in the reviewed evidence. The adapter diff at this head shows no import change, so the Option and DateTime references rely on imports already present at the merge base.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
Claude Code (Claude Opus 5.5) is posting this comment for the PR author.
I merged current main into the branch (29c81724). The diff against main is still the same two files, +31/−2.
The earlier Agent context budget failure (claudeSystemAppendBytes: 5135 -> 5258) came from the PR being 254 commits behind. main had shrunk agentContextBudget.json since this branch was created, and this PR doesn't touch agent context. On the new head, Agent Context Budget, CI, Mobile Fingerprint, PR Vouch and PR Size all pass, and the branch merges cleanly.
The pr:gate-failed label seems to be left over from that run. Could a maintainer clear it? The bug report is #26265.
The automated pipeline stopped on this pull request and will not merge it:
the fix is written and reviewed, but proving it needs something no worker has:
Permanent blocker: verification requires OpenCode 2.x connected through first-party OpenAI OAuth to a ChatGPT subscription with an exhausted usage window; first-party ChatGPT OAuth usage, limit, and renewal flows are unavailable on every VM. This prevents observing reset-time retention, Resume at reset, Snooze until reset, and automatic recovery on both commits. Replay cannot substitute because the scenario runner excludes provider-specific behavior and usage-limit stops.
It stays open for a person to finish or close.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bugfixFix PR opened by an issue lead; enters the PR looppr:gate-failedA land gate failedsize:S10-29 changed lines (additions + deletions).vouch:unvouchedAuthor is not yet trusted in the VOUCHED list.
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When OpenCode runs an OpenAI model through a ChatGPT subscription and the Codex usage window is exhausted, the turn now fails as
usage_limit(since https://git.cafe/pingdotgg/t3code/pulls/18315) but withoutresetAt. The thread shows "Reset time unavailable; retry manually", the resume-at-reset and snooze actions are hidden, and auto-resume never arms.ChatGPT already says when the limit lifts. Its 429 body is:
{"error":{"type":"usage_limit_reached","message":"The usage limit has been reached","plan_type":"plus","resets_at":1788753614,"resets_in_seconds":57361}}OpenCode 2 forwards that body on
session.execution.failedaserror.response.body, but the adapter ignored it.Change
When the OpenCode 2 adapter classifies a failure as a usage limit, it decodes
error.resets_at(Unix seconds) fromresponse.bodyand passes it asresetAttomakeProviderFailure, which already validates it and keeps it only forusage_limit. Claude limits keep the message-based reset from https://git.cafe/pingdotgg/t3code/pulls/29301, which takes precedence. Other bodies without that field (OpenCode Go, Zen, other providers) still produce no reset time, as before. No contract, client, or orchestration changes; the existing reset UI and auto-resume worker pick it up.Scope and approval
Fixes https://git.cafe/pingdotgg/t3code/issues/26265.
Follow-up to https://git.cafe/pingdotgg/t3code/issues/16366, which was closed by https://git.cafe/pingdotgg/t3code/pulls/18315 with the note that "OpenCode doesn't send a reset time in these events, so none is shown yet". For ChatGPT-backed models it does send one, in the response body. This is a small, focused fix in one adapter branch: it fills a field the failure contract already has, for the one provider shape that reports it. Separate from the Codex-provider fix in https://git.cafe/pingdotgg/t3code/pulls/18721 / https://git.cafe/pingdotgg/t3code/issues/14169.
Verification
classifies $name as $failureClasstable inOpenCode2AdapterV2.test.ts, expectingresetAt: "2026-10-27T15:25:41.000Z". With the adapter change stashed it fails on exactly that row (missingresetAt); the other rows pass. With the change, the file passes (89/89 after merging currentmain).vp run check:changed: format, lint, knip, typecheck (36 packages), and the changed test file all pass.responseBodyon an OpenCode 1.x message). I confirmed that OpenCode 2.1.3's execution-failure mapper includesresponse: { body }when the HTTP error has a body, and the@opencode/schema2.0.28Session.StructuredErrorused here declaresresponse.body.Implementation, tests, and PR: Claude Opus 5.5 via Claude Code in T3 Code.
🤖 Generated with Claude Code
Fixes #26265