siyuan-note/siyuan · error

streamed function call is missing from the terminal response

Error message

streamed function call is missing from the terminal response

What it means

The adapter accumulates function_call deltas keyed by output index in stream.responseToolCalls. The terminal response's Output array must contain function_call items at exactly those indices. A streamed index with no terminal counterpart means the deltas and the final response disagree about which calls exist, so the adapter aborts rather than emit a tool call whose final state is unknown.

Source

Thrown at kernel/util/openai_completion.go:505

func (stream *OpenAICompletionStream) queueResponseTerminal(response openai.CreateResponseResponse) error {
	if err := responseResultError(response); err != nil {
		return err
	}
	terminalToolCalls := map[int]struct{}{}
	for index, raw := range response.Output {
		item, ok := responseOutputItem(raw)
		if !ok || item.Type != "function_call" {
			continue
		}
		if item.Status == "incomplete" || item.Status == "in_progress" ||
			(response.Status == openai.ResponseStatusIncomplete && item.Status != "completed") {
			return errors.New("response ended with an incomplete function call")
		}
		terminalToolCalls[index] = struct{}{}
	}
	for index := range stream.responseToolCalls {
		if _, ok := terminalToolCalls[index]; !ok {
			return errors.New("streamed function call is missing from the terminal response")
		}
	}
	output, err := MarshalOpenAIResponseOutput(response.Output)
	if err != nil {
		return err
	}
	stream.responseOutput = output

	for index, raw := range response.Output {
		item, ok := responseOutputItem(raw)
		if !ok || item.Type != "function_call" {
			continue
		}
		current := stream.responseToolCalls[index]
		if current.ID != "" && item.CallID != "" && current.ID != item.CallID {
			return errors.New("streamed function call ID does not match the terminal response")
		}
		if current.Function.Name != "" && item.Name != "" && current.Function.Name != item.Name {

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Retry the request — assembly races are often transient on gateways.
  2. Reproduce against the official endpoint; if clean there, upgrade or replace the gateway.
  3. Use non-streaming requests (CreateOpenAICompletion) for tool-heavy conversations on that provider.
  4. Capture an SSE trace and report it to the gateway maintainers.

Example fix

// before: streaming tool calls through a flaky gateway
{"apiBase": "https://my-gateway/v1", "protocol": "openai-responses", "stream": true}

// after: same gateway, terminal output as the single source of truth
{"apiBase": "https://my-gateway/v1", "protocol": "openai-responses", "stream": false}
Defensive patterns

Strategy: fallback

Try / catch

On mismatch errors, treat the whole stream as unusable — never execute tool calls assembled from deltas — and rerun the step non-streaming so the terminal output is the single source of truth.

Prevention

When it happens

Trigger: Providers that reorder or drop items between delta emission and final assembly; a message item inserted into the terminal output shifting indices; gateways merging or regenerating events from multiple upstream attempts.

Common situations: OpenAI-compatible gateways with index-handling bugs on parallel tool calls; mid-stream provider failover changing assembly behavior.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/d042fdd162143cd4. Report an issue: GitHub.