Skip to content

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

@proyectoauraorg

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions