Repository navigation
fix(sse): stop retry wave on rate-limited 429 and drain 429 once - #13657
Merged
diegosouzapw merged 12 commits intoSep 16, 2026
Merged
diegosouzapw merged 12 commits into
diegosouzapw merged 12 commits into
Conversation
4 of 5 tasks
maxmad64bis
force-pushed
the
fix/rate-limited-429
branch
from
September 14, 2026 16:38
e3310a5 to
e52b0d3
Compare
maxmad64bis
marked this pull request as ready for review
September 14, 2026 16:41
4 of 5 tasks
…fault off) Gates the classified-429 early stop from cheaper-inference#13657. With the flag off every opencode 429 keeps rotating to the next account (cheaper-inference#9611).
…real upstream 429 - The cross-account 429 wave is unchanged by default (free tier is per egress IP, cheaper-inference#9611); stopping at a classified 429 needs OPENCODE_RATE_LIMITED_429_EARLY_STOP. - When the flag stops the wave, the real upstream 429 is returned as-is: status, body, Retry-After and quota headers stay intact, so opencode-monthly-quota-resets-in and the quota header/body rules in providerErrorRules.ts still match. The synthetic unavailableResponse drain, the invented Retry-After: 1 and the x-opencode-retry-state header are gone. - classifyUpstream429 checks Retry-After first and only then reads a bounded 8 KiB prefix from a clone, cancelling the clone reader; the original body is never consumed. classify429 drops its unused status input. - The rate-limited account is still cooled down before the stop.
5 tasks done
maxmad64bis
force-pushed
the
fix/rate-limited-429
branch
from
September 15, 2026 23:57
e52b0d3 to
8c02afa
Compare
…ailures (cheaper-inference#13615) Behind the new `OPENCODE_TRANSIENT_FAILOVER_BACKOFF` flag (default off), after two consecutive transient upstream failures the opencode rotation pauses before each later account (1.5s, 3s, 6s, capped at 10s per request) instead of hammering the upstream. Maintainer rework before merge (kept the idea, no default behavior change): - The pause honors the client abort signal (no dispatch after a disconnect), the failed attempt's body is cancelled before sleeping, `transientRetryDelayMs` now uses its arguments, and the sleep is injectable so the tests run without real timers. Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests. Thanks @maxmad64bis!
…o clear auth failure (cheaper-inference#13609) Behind the new `MISTRAL_AMBIGUOUS_401_SOFT_LOCKOUT` flag (default off), a bare Mistral 401 (`{"detail":"Unauthorized"}`, identical for a revoked key and an exhausted quota) gets a retryable cooldown instead of parking the connection as `expired`; after three soft strikes within an hour the next bare 401 parks it, so revocation still converges. Maintainer rework before merge (kept the idea, no default behavior change): - The predicate is shared with the connection-test module instead of duplicated; the squeezed 139-char line that dodged the file-size gate is formatted normally and the growth is rebaselined honestly with an annotation. Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests. Thanks @maxmad64bis!
# Conflicts: # docs/reference/FEATURE_FLAGS.md # open-sse/executors/opencode.ts # src/shared/constants/featureFlagDefinitions.ts # src/shared/utils/featureFlags.ts # tests/unit/feature-flags-settings.test.ts # tests/unit/server-owned-tool-loop-flag.test.ts
…ounts, stale suppressions)
…d ERROR_TYPE_CONTRACT import left by the batch merges (cheaper-inference#13816) Merged with admin on local + CI evidence: `tests/unit/i18n-catalogs-no-duplicate-keys.test.ts` red on the tip (59 catalogs) → `pass 3 / fail 0` here; **API Route Typecheck passes on this PR** (it fails on every PR based on the current tip because of the duplicated `ERROR_TYPE_CONTRACT` import this removes); CodeQL, semgrep, Vitest fast-path, Docs gates, Change Classification pass. The remaining red checks (Fast Quality Gates, Merge integrity, Unit Tests fast-path 1/2/4) are the same inherited tip reds every PR on release/v3.8.51 shows right now — cheaper-inference#13747 sweeps them. Both removed lines were byte-identical duplicates; nothing parsed or typed changes.
…work of cheaper-inference#13657 (tree unchanged)
diegosouzapw
merged commit Sep 16, 2026
997cd4d
into
cheaper-inference:release/v3.8.51
3 of 7 checks passed
This was referenced Sep 16, 2026
Merged
5 tasks done
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…aper-inference#13657) The opencode executor classifies rate-limited 429 bodies (`classify429`, with real tests) and, when a whole account wave is exhausted, returns the last real upstream 429 — status, body, `Retry-After` and quota headers intact — so the provider error rules (monthly-quota cooldown) keep working. Maintainer rework before merge (kept the idea, no default behavior change): - The original stopped the cross-account wave at the first classified 429 and replaced the response with a synthetic one that dropped the body and headers; stopping early is now opt-in behind `OPENCODE_RATE_LIMITED_429_EARLY_STOP` (default off), the rate-limited account is still cooled down, the body is read as a bounded 8 KiB prefix from a clone and the original is never consumed, and the unused `status` input is gone. Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 d61b804: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests. Thanks @maxmad64bis!
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
A chat request can die with a bare 500 after trying every account one by one. The loop now stops at the first proven rate limit, spares the remaining accounts, and returns a 429 with the relayed
Retry-Afterinstead. Bursts and 5xx keep rotating as before (transient 5xx).Related Issues
5xx/cut).breakon classified cause only).Validation
npm run lint— clean on the touched files; the full run is red on the base (inherited, out of scope)Tests Added Or Updated
tests/unit/opencode-rate-limited-classify.test.ts(9 tests green:Retry-Aftersorts rate-limited, body signals sort rate-limited, anything else sorts burst, 5xx never classified) plus router branch on fixture (classified 429 stops after 1 dispatch, spared accounts counted).typecheck:coreclean, targeted eslint clean, file-size OK, new-code complexity green.Coverage Notes
Health gate green on the touched accounts (no two live ones share a route). Error bodies captured into fixtures for the signal list.
Reviewer Notes