siyuan-note/siyuan · error

response ended with an incomplete function call

Error message

response ended with an incomplete function call

What it means

This is a consistency check in the OpenAI Responses-API-to-Chat-Completions streaming adapter (kernel/util/openai_completion.go). When the terminal event (response.completed / response.incomplete) arrives, queueResponseTerminal inspects every function_call item in the final response output; if a function call is still 'incomplete', 'in_progress', or the overall response status is 'incomplete' with a non-completed function call, it aborts the stream instead of emitting a truncated tool call that the agent might execute. It protects the agent runtime from running half-formed tool invocations.

Source

Thrown at kernel/util/openai_completion.go:557

			return openai.ChatCompletionStreamResponse{}, responseEventError(event, "response stream failed")
		}
		return response, nil
	}
}

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

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Increase the model's max output token budget so the function call can complete (set a higher MaxTokens/MaxCompletionTokens on the request).
  2. Retry the request; transient provider truncation is often resolved on a subsequent call.
  3. Reduce prompt/tool-schema size so fewer tokens are spent before the tool call completes.
  4. Handle the error in the agent loop by treating the turn as failed and re-prompting, rather than parsing the partial tool call.

Example fix

// before
resp, err := client.Chat.Completions.Create(...) // MaxTokens: 512
// after
resp, err := client.Chat.Completions.Create(...) // MaxTokens: 4096 (room for full tool call arguments)
Defensive patterns

Strategy: try-catch

Try / catch

resp, err := stream.Recv()
if err != nil {
    if strings.Contains(err.Error(), "incomplete function call") {
        // treat turn as failed; re-issue request with a larger token budget
        return retryWithLargerBudget(ctx)
    }
    return err
}

Prevention

When it happens

Trigger: Streaming via OpenAICompletionStream.Recv() over the Responses API when the response terminates with status 'incomplete' (e.g. max_output_tokens hit, content filter) while a function_call output item is present and not marked 'completed'.

Common situations: Model hits the max_output_tokens limit mid-way through emitting tool call arguments; provider truncates the response; content filter cuts off the turn while the model was calling a tool; a proxy returns a non-standard function_call item status.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/50ad6e17bd02025c. Report an issue: GitHub.