Skip to content

fix(codex): keep the tool name on streamed tool calls - #402

Merged
caigee-cmd merged 2 commits into
caigee-cmd:mainfrom
yoruuuchan:fix/codex-stream-tool-name
Oct 26, 2026
Merged

caigee-cmd merged 2 commits into
caigee-cmd:mainfrom
yoruuuchan:fix/codex-stream-tool-name

Conversation

@yoruuuchan

Copy link
Copy Markdown
Contributor

Summary

  • A streamed Codex tool call goes out with an empty function.name. The Responses stream puts the name on response.output_item.added's item; the function_call_arguments.* events that follow carry only item_id. sse.go parsed the item for type/status only, so the chunk that announces the call sent name: "".
  • Consequence for a client: it cannot dispatch a call whose name is empty (DSH reports unknown tool ""), and once that empty name is replayed in the request history the upstream rejects every later turn with Invalid 'input[6].name': empty string. Expected a string with minimum length 1, but got an empty string instead.
  • Fix: record item.name / item.call_id per output_index when the item is a function_call (or custom_tool_call), and use them when the call is announced. Non-tool items are skipped, so a reasoning item neither takes a tool-call index nor emits a chunk.

Reproduction

Same request, one field differs:

mode returned tool call
stream: false {"id":"call_…","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"Tokyo\"}"}}
stream: true {"index":0,"id":"fc_…","type":"function","function":{"name":"","arguments":"{\"city\":\"Tokyo\"}"}}

Test plan

  • go test ./internal/providers/codex/ -count=1 — green
  • go vet ./... — green
  • go test ./... — not green in this environment, on origin/main as well: internal/api, internal/executor, internal/providers/qoder, internal/runtime, internal/store fail on a clean f16d802 worktree (Windows path handling and a missing qodercli in PATH). internal/providers/codex passes both before and after this diff.
  • cd worker && npm test — no worker changes in this diff
  • cd frontend && npm run build && npm run lint — no frontend changes in this diff
  • Added changelog/unreleased/codex-stream-tool-name.md
  • No tokens, auth blobs, raw captures, or host details in the diff

New tests in internal/providers/codex/sse_test.go:

  • TestRewriteStreamKeepsToolNameFromOutputItem — fails before the change with "name":"" in the announcing chunk, passes after.
  • TestRewriteStreamIgnoresNonToolOutputItems — a reasoning item must not consume tool-call index 0 or emit a chunk.

The Responses stream carries a function call's name on
response.output_item.added, while the function_call_arguments events that
follow carry only the item id. The relay parsed the item for type and status
only, so the chunk announcing the call went out with an empty name: clients
could not dispatch it, and a client that replayed that empty name in history
had every later request rejected with
"Invalid 'input[n].name': empty string".

Record the name and call_id from the item and use them when the call is
announced. Non-tool items are skipped so a reasoning item neither consumes a
tool-call index nor emits a chunk.

@caigee-cmd caigee-cmd left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty function.name on streamed Codex tool calls is a real client-breaking bug: the name lives on response.output_item.added, and the argument events that follow only carry item_id. Recording name / call_id there, skipping non-tool items so a reasoning item cannot steal index 0, and covering both with tests is the right fix.

Changelog fragment is bilingual. Approving.

@caigee-cmd
caigee-cmd merged commit 1ff7ee2 into caigee-cmd:main Oct 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants