Skip to content

Zen gateway (/go/v1) streaming: tool_calls continuation deltas send "id":null / "function":{"name":null} instead of omitting absent fields #43328

Closed as not planned

Description

@suoweikeji-liqiang

Description

Streaming chat completions from the Zen gateway (https://opencode.ai/zen/go/v1/chat/completions) emit tool_calls continuation deltas with explicit null values for id and function.name instead of omitting the absent fields, as the OpenAI streaming format specifies.

The first tool_calls delta of a call carries the real values, e.g.:

"delta":{"role":null,"content":"","tool_calls":[{"index":0,"id":"chatcmpl-tool-b4d3cf2ed9145904","type":"function","function":{"name":"calculator","arguments":""}}]}

but every continuation delta for the same call sends:

"delta":{"role":null,"content":"","tool_calls":[{"index":0,"id":null,"type":null,"function":{"name":null,"arguments":""}}]}

This breaks strict OpenAI-compatible clients that accumulate deltas and treat any present field as an update. Concrete case: DeepSeek Harness (dsh) accumulates if (call.function?.name !== undefined) block.name = call.function.name — the null overwrites the previously accumulated name, and every tool call ends up dispatched as unknown tool "". The same request non-streamed returns a correct message.tool_calls[] with id and function.name intact.

Observed model: deepseek-v4-flash (stream mode). The behavior is intermittent across time windows — client-side session logs show tool calls failing on 2026-08-17 ~04:34–12:08 UTC and 2026-08-19 ~02:37–02:50 UTC, while requests outside those windows (e.g., all of 2026-08-18) received spec-compliant chunks with the fields omitted. That pattern suggests different upstream backends behind the gateway, some of which emit null-filled deltas.

Plugins

n/a

OpenCode version

n/a — Zen gateway (opencode.ai/zen/go/v1), captured 2026-08-19 02:56 UTC

Steps to reproduce

curl -N https://opencode.ai/zen/go/v1/chat/completions \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4-flash",
    "messages": [{"role":"user","content":"Use the calculator tool to compute 2+2"}],
    "tools": [{"type":"function","function":{"name":"calculator","description":"compute an arithmetic expression","parameters":{"type":"object","properties":{"expr":{"type":"string"}},"required":["expr"]}}}],
    "tool_choice": "auto",
    "max_tokens": 200,
    "stream": true
  }'

The streamed SSE shows the chunk sequence quoted above: id/name present on the first tool_calls delta, explicit null on the continuation deltas.

Expected: continuation deltas omit id / type / function.name when they carry no value (per the OpenAI streaming format), so field-presence-based delta accumulators keep the values from the first delta.

Screenshot and/or share link

n/a (raw SSE text)

Operating System

macOS 25.5 (arm64), curl — issue is server-side

Activity

  1. github-actions commented on Aug 19, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. github-actions commented on Oct 19, 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

No one assigned

    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