Client or integration
Codex App
Provider or upstream service
Xhub (api15.xhub.chat), through generic openai-responses providers
OpenCodex version
2.56.0
Endpoint or capability
/v1/responses, streaming, strategy: "failover" combos
Current behaviour
A provider can return HTTP 200 with text/event-stream and then emit a bare SSE error before any text, reasoning, tool call, or other meaningful output. A representative redacted frame is:
event: error
data: {"type":"error","error":{"type":"server_error","code":"upstream_server_error","message":"temporarily unavailable"}}
The combo stream preflight does not classify this default bare type: "error" payload as a retryable zero-output terminal. Because an unknown event commits the child conservatively, the combo returns the failed stream instead of trying the next eligible target. In VS Code 1.137.0 / Copilot Chat 0.65.0 this surfaced as The model provider reported a failed response without any error details (event: error).
By contrast, ordinary pre-stream HTTP 503 responses in the same setup do traverse the remaining combo targets.
Expected behaviour
When a bare SSE error arrives before the combo commit boundary and contains concrete retryable provider evidence, such as a structured 429 or 5xx status/code, the combo should classify that attempt through the existing bounded failure policy and may advance to the next eligible target.
Unknown bare errors, request/policy errors, and every error after text, reasoning, tool/action, or other output begins should remain fail-closed and must not be replayed. This matches the replay-safety contract established for zero-output response.failed in #2431/#2449.
Minimal redacted request or reproduction
# Configure combo/repro with two openai-responses targets and strategy: failover.
# Target A returns HTTP 200, Content-Type: text/event-stream, then only:
#
# event: error
# data: {"type":"error","error":{"type":"server_error","code":"upstream_server_error","message":"temporarily unavailable"}}
#
# Target B returns a normal Responses SSE completion.
curl -N http://127.0.0.1:10100/v1/responses \
-H "content-type: application/json" \
-d "{\"model\":\"combo/repro\",\"input\":\"reply with ok\",\"stream\":true}"
# Actual: target A is committed and its error reaches the client; B is not attempted.
# Expected: A is recorded as a retryable zero-output failure and B is attempted.
Actual response or error
The model provider reported a failed response without any error details (event: error).
OpenCodex service logs do not retain the raw upstream SSE frame. The frame above is a redacted representative of the observed event: error / type: "error" shape; no credentials, affected request bodies, or private provider identifiers are included.
Upstream documentation
No public Xhub specification for this SSE error envelope was found. The compatibility requirement is concrete client behaviour plus the existing OpenCodex combo contract: retryable failures before meaningful output may fail over, while post-output failures must not replay.
Related contract and prior fix: #2431 and #2449.
Suggested mapping or implementation notes
src/server/responses/combo-stream-preflight.ts already supports an explicit caller predicate opting a known bare type: "error" payload into replay, but the default retryableZeroOutputTerminal() accepts only response.failed and the allowlisted transport response.incomplete reasons.
A conservative implementation could:
- recognize only bare error payloads with structured retryable status/code evidence;
- normalize them through
failedTerminalResponse() and the existing combo failure classifier;
- preserve the existing
outputCommitted and bounded-buffer barriers;
- leave unstructured or ambiguous bare errors committed.
This should be provider-neutral rather than keyed to Xhub.
Additional context and attachments
Environment: macOS 15.2 arm64, OpenCodex 2.56.0, VS Code 1.137.0, Copilot Chat 0.65.0.
Existing related work:
No matching open or closed issue was found for default combo handling of bare type: "error" SSE events.
Checks
Client or integration
Codex App
Provider or upstream service
Xhub (
api15.xhub.chat), through genericopenai-responsesprovidersOpenCodex version
2.56.0
Endpoint or capability
/v1/responses, streaming,strategy: "failover"combosCurrent behaviour
A provider can return HTTP 200 with
text/event-streamand then emit a bare SSE error before any text, reasoning, tool call, or other meaningful output. A representative redacted frame is:The combo stream preflight does not classify this default bare
type: "error"payload as a retryable zero-output terminal. Because an unknown event commits the child conservatively, the combo returns the failed stream instead of trying the next eligible target. In VS Code 1.137.0 / Copilot Chat 0.65.0 this surfaced asThe model provider reported a failed response without any error details (event: error).By contrast, ordinary pre-stream HTTP 503 responses in the same setup do traverse the remaining combo targets.
Expected behaviour
When a bare SSE error arrives before the combo commit boundary and contains concrete retryable provider evidence, such as a structured 429 or 5xx status/code, the combo should classify that attempt through the existing bounded failure policy and may advance to the next eligible target.
Unknown bare errors, request/policy errors, and every error after text, reasoning, tool/action, or other output begins should remain fail-closed and must not be replayed. This matches the replay-safety contract established for zero-output
response.failedin #2431/#2449.Minimal redacted request or reproduction
Actual response or error
OpenCodex service logs do not retain the raw upstream SSE frame. The frame above is a redacted representative of the observed
event: error/type: "error"shape; no credentials, affected request bodies, or private provider identifiers are included.Upstream documentation
No public Xhub specification for this SSE error envelope was found. The compatibility requirement is concrete client behaviour plus the existing OpenCodex combo contract: retryable failures before meaningful output may fail over, while post-output failures must not replay.
Related contract and prior fix: #2431 and #2449.
Suggested mapping or implementation notes
src/server/responses/combo-stream-preflight.tsalready supports an explicit caller predicate opting a known baretype: "error"payload into replay, but the defaultretryableZeroOutputTerminal()accepts onlyresponse.failedand the allowlisted transportresponse.incompletereasons.A conservative implementation could:
failedTerminalResponse()and the existing combo failure classifier;outputCommittedand bounded-buffer barriers;This should be provider-neutral rather than keyed to Xhub.
Additional context and attachments
Environment: macOS 15.2 arm64, OpenCodex 2.56.0, VS Code 1.137.0, Copilot Chat 0.65.0.
Existing related work:
response.failedcombo failover (merged)response.incompletefailover (merged)No matching open or closed issue was found for default combo handling of bare
type: "error"SSE events.Checks