Skip to content

bug: processToolCalls() ignores finish_reason "length" and stream-end without finish_reason, causing missing nativeArgs #425

Description

@proyectoauraorg

Problem

When a streaming tool call is interrupted by token exhaustion, tool calls end up with empty nativeArgs and the user sees:

Invalid tool call for 'write_to_file': missing nativeArgs. This usually means the model streamed invalid or incomplete arguments and the call could not be finalized.

This affects any OpenAI-compatible provider when the output token limit is hit mid-tool-call — it is not provider-specific.

Root Cause

1. processToolCalls() only handles finish_reason: "tool_calls"

File: src/api/providers/openai.ts:497

if (finishReason === "tool_calls" && activeToolCallIds.size > 0) {
    for (const id of activeToolCallIds) {
        yield { type: "tool_call_end", id }
    }
    activeToolCallIds.clear()
}

This does NOT handle:

  • finish_reason: "length" — standard OpenAI behavior when max_tokens is reached mid-tool-call
  • Stream termination without any finish_reason — behavior of some OpenAI-compatible proxies (e.g., MiMo)

2. No post-loop cleanup in stream handlers

Both handleStreamResponse() (openai.ts:437-463) and MimoHandler.createMessage() (mimo.ts:110-142) end their for await loops without any cleanup of activeToolCallIds. If processToolCalls() never emitted tool_call_end, the tool calls stay as partials forever.

3. Error message lacks actionable guidance

presentAssistantMessage.ts:422-424 reports the error but gives the user no hint about what to do.

Reproduction

Direct API reproduction (curl)

With max_tokens:100 (forces premature termination):

curl -s -X POST https://token-plan-sgp.xiaomimimo.com/v1/chat/completions   -H "Content-Type: application/json"   -H "Authorization: Bearer <KEY>"   -d '{
    "model":"mimo-v2.5-pro",
    "messages":[{"role":"user","content":"Use write_to_file to create file test.txt with 2000 chars of lorem ipsum"}],
    "tools":[{"type":"function","function":{"name":"write_to_file","parameters":{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}}}],
    "max_tokens":100,
    "stream":true,
    "stream_options":{"include_usage":true}
  }'

Result: Stream ends with {"choices":[]} — no finish_reason, empty choices array. Only 55 tokens available for tool call after reasoning.

Standard OpenAI behavior

When OpenAI hits max_tokens mid-tool-call, it emits finish_reason: "length". Zoo Code's processToolCalls() ignores this entirely, leaving tool calls unfinalized.

Scenario OpenAI Standard MiMo Proxy
Token limit hit mid-tool-call finish_reason: "length" ❌ ignored choices: [] (no finish_reason) ❌ ignored
Tool call completed finish_reason: "tool_calls" finish_reason: "tool_calls"
Normal stop finish_reason: "stop" finish_reason: "stop"

Proposed Fix

Fix 1 (P0): Handle finish_reason: "length" in processToolCalls()

File: src/api/providers/openai.ts:497

// Before:
if (finishReason === "tool_calls" && activeToolCallIds.size > 0) {

// After:
if ((finishReason === "tool_calls" || finishReason === "length") && activeToolCallIds.size > 0) {

Fix 2 (P0): Add post-loop cleanup in stream handlers

File: src/api/providers/openai.tshandleStreamResponse() after the for await loop (line 462):

// Finalize any tool calls that weren't explicitly ended
if (activeToolCallIds.size > 0) {
    for (const id of activeToolCallIds) {
        yield { type: "tool_call_end", id }
    }
    activeToolCallIds.clear()
}

File: src/api/providers/mimo.tscreateMessage() after the for await loop (line 142):

// Finalize any tool calls that weren't explicitly ended by finish_reason
for (const id of activeToolCallIds) {
    yield { type: "tool_call_end", id }
}
activeToolCallIds.clear()

Fix 3 (P1): Improve error message

File: src/core/assistant-message/presentAssistantMessage.ts:422-424

const errorMessage =
    `Invalid tool call for '${block.name}': missing nativeArgs. ` +
    `This usually means the model ran out of output tokens before completing the tool arguments. ` +
    `Try: (1) simplifying the request, (2) breaking it into smaller steps, ` +
    `or (3) increasing the model's max output tokens in settings.`

Files to Modify

File Change Priority
src/api/providers/openai.ts Handle finish_reason: "length" + post-loop cleanup P0
src/api/providers/mimo.ts Post-loop cleanup after stream P0
src/core/assistant-message/presentAssistantMessage.ts Better error message P1
src/api/providers/__tests__/openai.spec.ts Tests for new behavior P1
src/api/providers/__tests__/mimo.spec.ts Integration test for premature stream termination P1

Related Issues

Alignment with CONTRIBUTING.md

Reliability First: Ensure diff editing and command execution are consistently reliable. Expand robust support for a wide variety of AI providers and models.

This fix directly improves reliability for all OpenAI-compatible providers when output tokens are exhausted mid-tool-call.

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