Skip to content

[Provider compatibility]: zero-output bare SSE error commits a failover combo target #4808

Description

@87003697

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:

  1. recognize only bare error payloads with structured retryable status/code evidence;
  2. normalize them through failedTerminalResponse() and the existing combo failure classifier;
  3. preserve the existing outputCommitted and bounded-buffer barriers;
  4. 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

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportsstreamingSSE, WebSocket, terminal stream framestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions