Tool call streaming broken in 0.64.0 with local LLM providers

Resolved 💬 14 comments Opened Dec 3, 2025 by latent-variable Closed Dec 9, 2025
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Codex 0.64.0 fails to parse streaming tool calls from local LLM providers (LM Studio, etc.), treating each character/chunk of the tool call arguments as a separate tool call instead of accumulating them into a single call.

Environment

  • Codex version: 0.64.0 (broken), 0.63.0 (working)
  • LLM Provider: Local provider using OpenAI-compatible API (localhost:5002/v1)
  • Model: GPT-OSS-120B
  • OS: macOS
  • Installation: npm via nvm

Expected Behavior

Tool calls should be accumulated from streaming chunks into a single function call with complete arguments:

{
  "type": "function_call",
  "name": "shell",
  "arguments": "{\"command\": [\"bash\", \"-lc\", \"ls -R\"], \"workdir\": \"/Users/.../test\"}",
  "call_id": "701601222"
}

Actual Behavior in 0.64.0

Tool calls are fragmented into dozens of separate calls:

  1. First call has name but empty arguments:

``json
{"name": "shell", "arguments": "", "call_id": "701601222"}
``

  1. Then 40+ separate calls with empty names and single-character arguments:

``json
{"name": "", "arguments": "{\n", "call_id": "tool-call-1"}
{"name": "", "arguments": " ", "call_id": "tool-call-2"}
{"name": "", "arguments": " \"", "call_id": "tool-call-3"}
{"name": "", "arguments": "command", "call_id": "tool-call-4"}
// ... continues for each character/small chunk
``

Error Messages

All fragmented tool calls fail with:

  • First call: "failed to parse function arguments: Error(\"EOF while parsing a value\", line: 1, column: 0)"
  • Subsequent calls: "unsupported call: "

Steps to Reproduce

  1. Configure Codex to use local LLM provider with OpenAI-compatible API
  2. Use any model that supports tool calls
  3. Ask Codex to perform any task requiring tool calls
  4. Observe fragmented tool calls in session logs

Analysis

This appears to be a regression in how Codex 0.64.0 handles streaming tool call deltas from OpenAI-compatible /v1/chat/completions endpoints. Instead of accumulating streaming chunks with the same tool call index into a single call, each chunk is being treated as a new, separate tool call.

The issue affects local LLM providers that stream tool call arguments incrementally.

Workaround

Downgrade to Codex 0.63.0:

npm install -g @openai/codex@0.63.0

Additional Context

  • Issue does not occur in Codex 0.63.0 with same setup
  • Using standard OpenAI-compatible API format
  • No configuration changes between working (0.63.0) and broken (0.64.0) versions
  • Only the Codex update triggered this issue

View original on GitHub ↗

14 Comments

github-actions[bot] contributor · 7 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #7275
  • #7094
  • #7051
  • #6834

Powered by Codex Action

latent-variable · 7 months ago

Reviewed the potential duplicates:

  • #7275 - Different issue: validation errors with third-party providers, not about streaming fragmentation
  • #7094 - Different issue: empty function names rejected by Azure, was identified as a LiteLLM bug (already fixed)
  • #7051 - Different issue: custom API problems after 0.59 upgrade, not specific to 0.64.0 streaming
  • #6834 - Different issue: infinite function call loop with token usage

This issue is specific to Codex 0.64.0 streaming tool call regression where arguments are fragmented into dozens of separate calls instead of being accumulated. Works fine in 0.63.0. Not a duplicate.

etraut-openai contributor · 7 months ago

Thanks for reporting. This is a duplicate of #7051. As a workaround, try switching to wire_api = "responses". This newer API is recommended over the older "chat" API.

latent-variable · 7 months ago

Update: Tried the wire_api = "responses" workaround suggested, but it doesn't work with local LLM providers like LM Studio.

Error with wire_api = "responses":

{
  "error": {
    "message": "Invalid discriminator value. Expected 'function' | 'mcp'",
    "type": "invalid_request_error",
    "param": "tools.5.type",
    "code": "invalid_union_discriminator"
  }
}

Root Cause:

The wire_api = "responses" setting appears to be intended for OpenAI's actual Responses API, not for local LLM providers. LM Studio's /v1/responses endpoint expects different tool formatting than what Codex sends when using this setting.

Current Status:

  • 0.63.0 with default chat API: Works fine ✅
  • 0.64.0+ with default chat API: Streaming tool calls fragmented into dozens of separate calls ❌
  • 0.64.0+ with wire_api="responses": Tool format errors with local providers ❌

The streaming fragmentation issue is specific to local LLM providers using the OpenAI-compatible chat completions endpoint and persists in 0.65.0.

This is not the same as #7051 - that issue is about corporate proxies that don't support the responses API. This issue is about local LLM providers where the chat API streaming changed behavior in 0.64.0+.

etraut-openai contributor · 7 months ago

@latent-variable, could you provide more details about your configuration including the model you're using in LM Studio and your config.toml settings for that model? I'd like to repro what you'd seeing. Thanks!

etraut-openai contributor · 7 months ago

And yes, I agree this isn't the same as #7501. It may be related to #7579.

latent-variable · 7 months ago

@etraut-openai Here are the details:

Model

LM Studio model: openai/gpt-oss-120b (GGUF format)

Configuration

config.toml settings:

[model_providers.lms]
name = "LM Studio"
base_url = "http://localhost:5002/v1"
request_max_retries = 4

[profiles.gpt-oss-120b-lms]
model_provider = "lms"
model = "openai/gpt-oss-120b"

LM Studio Settings

  • LM Studio running on localhost:5002
  • Using standard OpenAI-compatible /v1/chat/completions endpoint
  • Model loaded with default settings (no special chat template modifications)

Reproduction

  1. Install Codex 0.64.0 or 0.65.0
  2. Configure as above
  3. Run any command that requires tool calls (e.g., codex "list files in current directory")
  4. Observe fragmented tool calls in session logs at ~/.codex/sessions/

The session logs show tool calls split into 40+ fragments with empty names instead of one complete call.

Let me know if you need any other details!

joninco · 7 months ago

The Chat Completions SSE parser assumes id is present in every tool call delta, falling back to format!("tool-call-{}", tool_call_order.len()) when absent. However, OpenAI's streaming format only includes id in the first
delta—subsequent deltas use index for correlation. This causes each delta to create a new tool call entry instead of accumulating arguments into a single call.

Result: JSON parse failures, corrupted conversation history, and models hanging/erroring on the second turn.

Fix:
Use index field for delta correlation when id is absent:

let id = if let Some(id_str) = provided_id {
// First delta or servers that send id in every delta
id_str.to_string()
} else if let Some(idx) = provided_index {
// Subsequent deltas - look up stored id by index
tool_call_order[idx as usize].clone()
} else {
// Fallback
format!("tool-call-{}", tool_call_order.len())
};

Affected: llama.cpp, LM Studio, and other local providers using standard OpenAI streaming format.

etraut-openai contributor · 7 months ago

@latent-variable, can you confirm that you're using the latest version of LM Studio? I'm using 0.3.33, and I'm not able to repro the problem you're seeing with the "responses" endpoint. Earlier versions of LM Studio had bugs in its "responses" handling, so I wonder if you're on an older version.

latent-variable · 7 months ago

@etraut-openai Yes, I'm using the latest LM Studio version 0.3.33.

@joninco's analysis appears to be correct - the issue is with how Codex's Chat Completions SSE parser handles tool call deltas. The parser creates a new tool call for each delta instead of accumulating them using the index field.

To clarify: I'm not using the "responses" endpoint successfully - my earlier comment showed it fails with tool format errors. The issue occurs with the standard chat completions endpoint (/v1/chat/completions) which LM Studio implements following OpenAI's streaming format (id in first delta, index for correlation in subsequent deltas).

Current workaround: Downgrade to Codex 0.63.0, which correctly handles this streaming format.

latent-variable · 7 months ago

Additional detail that might help reproduce:

The issue only occurs when tool calls are involved. Simple chat responses work fine.

Works:

codex "say hi"

Model responds normally without issues.

Fails:

codex "read the README file"

Model attempts to call the Read tool, and that's when the streaming parser fragments the tool call into dozens of separate calls.

So this is specifically a tool call streaming bug, not a general chat streaming issue. The parser only breaks when processing tool_calls in the SSE stream.

etraut-openai contributor · 7 months ago

We have a fix for the index field issue in the prerelease version 0.66.0-alpha.7 if you'd like to try it out.

I'm trying to repro the problem you reported above with the "responses" endpoint. So far, I'm not seeing tool format errors with the latest version.

latent-variable · 7 months ago

Confirmed: 0.66.0-alpha.7 fixes the issue.

Tested with tool calls - no more fragmentation. Session logs show proper tool call accumulation with complete arguments and no parse errors.

Thanks for the quick fix!

etraut-openai contributor · 7 months ago

We've published 0.66.0, which I believe addresses the issue tracked in this bug report.

We're announcing that we plan to deprecate support for the chat/completions API in Codex and eventually remove support completely. For details, please refer to https://github.com/openai/codex/discussions/7782, which provides the rationale and timeline.