Repository navigation
fix(sse): skip model-only lockout for an isolated 5xx after the stream relayed output - #15186
Merged
diegosouzapw merged 4 commits intoOct 2, 2026
Conversation
…m relayed output A streamed response that already sent text, reasoning or a tool call to the client and then fails upstream (for example "Internal error during token generation" 45-80 s into the answer, or a stream timeout) reached markAccountUnavailable through onStreamFailure and recorded a model-only lockout. The client response is already committed, so the lock cannot help that request; it only takes the model away from every other client, and on a single-account provider it removes the model entirely until the lock expires. In production this was the source of most 5xx model-only lockouts for one such provider, while hundreds of concurrent streams on the same model succeeded in between. A model that fails every stream mid-way is still broken, so such failures are counted per provider/connection/model and the lock is recorded when 3 arrive in a row with no completed stream in between (a streak older than 10 min starts over). - streamTiming: register each SSE transform's timing in a WeakMap and expose streamEmittedOutput(stream), based on the existing firstOutputAt marker. - chatCore / streamFailureFinalization: stream failures now carry outputEmitted, read at failure time, so pipeline errors (terminated, stream active timeout) are covered as well as in-stream upstream errors. A stream that completes with 200 resets the streak. - accountFallback/postOutputFailureStreak: the streak counter. - chatHelpers / auth: pass streamOutputEmitted to markAccountUnavailable; the per-model branch treats an isolated post-output 5xx like the existing bare-500 and synthetic empty-stream cases (isRequestScopedServerFailure): lastError is still recorded, no lockout. buildExhaustionOptions now reuses the markAccountUnavailable option type instead of repeating it. Unchanged: a 5xx before any output (including a combo peek that sees the error before content and fails over), 429/404 lockouts, connection cooldown for connection-scoped providers, and the combo quality-failure lockout. stream.ts, chatCore.ts and chatHelpers.ts only gain call-site wiring; their file-size ceilings are bumped with a _rebaseline entry explaining each line. Tests: tests/unit/stream-failure-after-output-no-model-lockout.test.ts (isolated post-output 5xx: no lockout; 3 in a row: lockout; a completed stream resets the streak; streak keys and expiry; pre-output 5xx and post-output 429: lockout; registry in passthrough and translate modes, including an upstream error frame before any content not counting as output; finalizer plumbing). Reproduced end to end with a local OpenAI-compatible mock: before, a mid-stream error locked the model and the next request got 503; after, the next request succeeds, a pre-output error still locks, and three mid-stream failures in a row lock.
QuangBlue
force-pushed
the
fix/stream-output-model-lockout
branch
from
September 30, 2026 08:22
b7e9331 to
d51fcbe
Compare
added 3 commits
September 30, 2026 15:24
# Conflicts: # config/quality/file-size-baseline.json
…ut test for mutation runs
diegosouzapw
merged commit Oct 2, 2026
f690548
into
diegosouzapw:release/v3.8.52
46 of 51 checks passed
Owner
|
Thanks @QuangBlue — merged into |
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.
Problem
A streamed response that has already sent text, reasoning or a tool call to the client can still fail upstream: for example
Internal error during token generation45–80 s into the answer, or astream active timeout/terminatedpipeline error. That failure goesonStreamFailure→markAccountUnavailable, and for a per-model-scoped connection (passthroughModels) it records a model-only lockout.The client response is already committed, so the lock cannot help that request. It only takes the model away from every other client, and on a single-account provider it removes the model entirely until the lock expires.
In our deployment, over 8 days almost all 5xx model-only lockouts on one single-account provider were of this kind: 27 of 31 came after the stream had already relayed content. Hundreds of concurrent streams on the same model succeeded during the same period. Of the remaining 4, 3 were caught by the combo quality peek before any content, and 1 was a non-stream HTML 520.
Fix
streamTiming.ts: each SSE transform'sStreamTimingis registered in a WeakMap. A newstreamEmittedOutput(stream)reads the existingfirstOutputAtmarker (the first forwarded chunk carrying text, reasoning or a tool call).streamFailureFinalization.tsandchatCore.ts: stream failures now carryoutputEmitted, read at failure time. This covers pipeline errors (terminated,stream active timeout) as well as in-stream upstream errors.chatHelpers.tsandauth.ts:onStreamFailurepassesstreamOutputEmittedtomarkAccountUnavailable.isRequestScopedServerFailureinsyntheticEmptyStream.ts.lastErroris still recorded; no lockout is written.accountFallback/postOutputFailureStreak.ts). The lock is recorded when 3 arrive in a row with no completed stream in between. A stream that completes with 200 resets the count, and a streak older than 10 min starts over.buildExhaustionOptionsnow reuses themarkAccountUnavailableoption type instead of repeating it, soauth.tsshrinks by 7 lines.Unchanged
File-size ceilings
stream.ts(+1),chatCore.ts(+3) andchatHelpers.ts(+1) only gain irreducible call-site wiring. The ceilings are bumped infile-size-baseline.json, with a_rebaseline_…entry explaining why. The logic lives instreamTiming.ts,postOutputFailureStreak.tsandsyntheticEmptyStream.ts.Tests
tests/unit/stream-failure-after-output-no-model-lockout.test.ts:lastErrorTyperecorded;outputEmitted;typecheck:core,check-open-sse-typecheck,check-api-typecheck,check-file-size, and ~900 neighbouring unit tests (lockout, stream, chat, combo). The only failures are the same ones on the base without this change.503 all targets were skipped.