Skip to content

OpenAI Chat SSE parser ignores valid data:{...} frames without a space #1170

Description

@Aono255

Client or integration

Other — downstream application embedding the OpenCodex proxy.

Provider or upstream service

Private OpenAI-compatible model gateway (provider identity and endpoint redacted).

OpenCodex version

@bitkyc08/opencodex 2.7.42

Endpoint or capability

/v1/chat/completions streaming (SSE)

Current behaviour

The OpenAI Chat adapter only processes lines starting with data: (a colon followed by a space).

The upstream emits valid SSE data fields without that optional space, for example data:{...}. As a result, every JSON frame is ignored. A terminal chunk carrying finish_reason: "stop" and a trailing usage-only chunk are also ignored. When the upstream closes the stream, OpenCodex reports:

upstream stream ended without a terminal signal ([DONE] or finish_reason) — possible truncation

Expected behaviour

OpenCodex should accept both data:{...} and data: {...} as SSE data fields.

A non-empty OpenAI finish_reason should remain a valid terminal signal even when the upstream omits the OpenAI-specific data: [DONE] sentinel.

Minimal redacted request or reproduction

curl -N -sS "$BASE_URL/chat/completions" \
  -H "Content-Type: application/json" \
  -H "X-Example-Auth: <redacted>" \
  -d '{
    "model": "<model>",
    "messages": [{"role": "user", "content": "hello"}],
    "stream": true
  }'

The upstream response uses this framing pattern:

data:{"id":"<id>","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"..."},"finish_reason":null}]}

...

data:{"id":"<id>","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data:{"id":"<id>","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}

There is no data: [DONE] frame.

Actual response or error

MODEL_RUNTIME_CANARY_FAILED: turn status failed; stream disconnected before completion:
upstream stream ended without a terminal signal ([DONE] or finish_reason) — possible truncation

Upstream documentation

SSE field syntax permits the value to begin immediately after the colon; one optional U+0020 space is stripped when present:

https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation

The non-null finish_reason behavior is an OpenAI-compatible streaming convention. The upstream has no public provider-specific specification.

Suggested mapping or implementation notes

The shared data-line handler can accept the data: field name regardless of whether a space follows it:

if (!line.startsWith("data:")) return "continue";
const payload = line.slice("data:".length).trim();

Regression coverage should include:

  1. Unspaced data:{...} frames with a final non-null finish_reason and no [DONE] -> successful completion.
  2. Standard data: {...} framing -> unchanged behavior.
  3. A genuinely truncated stream with no [DONE], no final finish_reason, and no terminal usage frame -> still fail closed.

Additional context and attachments

The report is fully redacted. No production endpoint, model identifier, request content, or credentials are included.

  • 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

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportsstreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions