Skip to content

Responses API SSE stream starts with a synthetic chatcmpl-dummy frame causing TypeValidationError #30916

Closed as not planned

Description

@ben-wangz

Description

Summary

When using the OpenAI Responses API (sdk.responses()) with models served through certain OpenAI-compatible gateways, the SSE stream begins with a synthetic chatcmpl-dummy frame in Chat Completions format before the real Responses API events. This frame is not a valid Responses API event and causes a TypeValidationError that terminates the agent turn.

Environment

  • OpenCode version: 1.16.0 (dev), 1.15.13 (release)
  • Provider: OpenAI (@ai-sdk/openai)
  • Model: gpt-5.5 (via OpenAI-compatible gateway)
  • Runtime: ai-sdk

Reproducing

1. Verification command

Set up environment variables for your OpenAI-compatible provider:

export OPENAI_BASE_URL="<your-endpoint>/v1"
export OPENAI_API_KEY="<your-api-key>"

Run opencode with the Responses API (default) and Chat Completions API:

# Test with gpt-5.5 (Responses API — fails)
opencode run "say hi" --format json --model openai/gpt-5.5 --dangerously-skip-permissions

2. gpt-5.5 fails with TypeValidationError

The stream errors immediately:

{
  "type": "error",
  "error": {
    "name": "UnknownError",
    "data": {
      "message": "Type validation failed: Value: {\"id\":\"chatcmpl-dummy\",\"object\":\"chat.completion.chunk\",\"created\":1780651633,\"model\":\"gpt-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"}}]}.\nError message: [\n  {\n    \"code\": \"invalid_union\",\n    \"errors\": [\n      [\n        {\n          \"code\": \"invalid_value\",\n          \"values\": [\"response.output_text.delta\"],\n          \"path\": [\"type\"],\n          \"message\": \"Invalid input: expected \\\"response.output_text.delta\\\"\"\n        },\n        ...\n      ],\n      ...\n    ],\n    \"message\": \"Invalid input\"\n  }\n]"
    }
  }
}

The error shows chatcmpl-dummy — a Chat Completions format frame with "content":"" — being passed to the Responses API event validator, which rejects it because it does not match any expected event type (response.output_text.delta, response.created, etc.).

3. deepseek-v4-pro works fine

The same setup works correctly with DeepSeek's native API (which does not go through the same gateway):

opencode run "say hi" --format json --model deepseek/deepseek-v4-pro --dangerously-skip-permissions

Expected output:

{"type":"step_start","part":{"type":"step-start"}}
{"type":"text","part":{"type":"text","text":"hi"}}
{"type":"step_finish","part":{"type":"step-finish"}}

4. Root cause confirmed via curl

Probing the Responses API endpoint directly reveals the synthetic frame:

curl -N -s "${OPENAI_BASE_URL}/responses" \
  -H "Authorization: Bearer ${OPENAI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.5","input":"hello","stream":true}'

The very first SSE event is:

data: {"id":"chatcmpl-dummy","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}

Note the content: "" — this frame carries no actual content. It is a bare data: frame (no event: prefix) with object: "chat.completion.chunk" — it is not a valid Responses API event. The subsequent frames are proper Responses API events (event: response.created, event: response.output_text.delta, etc.).

The same test against DeepSeek's API shows no such frame:

curl -N -s "${DEEPSEEK_BASE_URL}/responses" \
  -H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-pro","input":"hello","stream":true}'

Output starts directly with a proper Responses API event — no chatcmpl-dummy prefix.

5. Suggested fix

The chatcmpl-dummy frame has a hardcoded ID ("chatcmpl-dummy"). The fix is to filter it out at the HTTP response level in the AI SDK's custom fetch wrapper, before the stream reaches the event validator.

Location: packages/opencode/src/provider/provider.ts — in the custom fetch wrapper inside resolveSDK().

Approach: When the response is an SSE stream from a /responses endpoint, pipe the body through a TransformStream that parses each SSE event's data: line as JSON and drops any event whose id equals "chatcmpl-dummy".

This approach:

  • Precisely targets only the synthetic chatcmpl-dummy frame
  • Does not affect normal Chat Completions or Responses API streams
  • Requires no user configuration changes
  • Is transparent to all other providers

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. rekram1-node commented on Jun 5, 2026

    @rekram1-node
    Collaborator

    sounds like ur gateway/proxy has a bug? What gateway/proxy is this

  2. ben-wangz commented on Jun 6, 2026

    @ben-wangz
    Author

    sounds like ur gateway/proxy has a bug? What gateway/proxy is this

    I use local sub2api v0.1.125 to manage my accounts. And I do not meet this problem except the gpt model.

  3. ben-wangz commented on Jun 10, 2026

    @ben-wangz
    Author

    updating PR fix:
    the filtering is now configurable through opencode.json via provider options, for example provider.openai.options.filterSseFrame.
    By default, no SSE frames are filtered. The filter only runs when filterSseFrame is explicitly configured, so existing users are unaffected.

  4. github-actions commented on Aug 10, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions