Codex Desktop + Kimi provider: all injected inputs (heartbeat/cron automations, thread delegation) fail with "tool_call_id is not found" and permanently poison the thread
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
On Codex Desktop for macOS with a custom Kimi provider (wire_api = "responses"), every turn whose input is delivered as a system-injected standalone function_call_output fails immediately with HTTP 400. This is the same root cause as #41690 (DeepSeek) and #41799 (Azure), but the blast radius is wider and the fallout is worse:
- All injection paths are affected, not just cron automations: heartbeat automations, cron automations,
create_threaddelegation (<codex_delegation>), andsend_message_to_threadall deliver their input as afunction_call_outputwith nocall_id, and all fail identically. - The orphaned item poisons the thread permanently: it is persisted into the rollout history, so every subsequent turn of that thread — including plain user-typed messages — fails with the same error. The thread is bricked until the rollout file is manually repaired.
Kimi's Responses endpoint rejects the request with:
{"error":{"code":"invalid_request_error","message":"Invalid request: tool_call_id is not found","type":"invalid_request_error"}}
(note the empty tool_call_id — double space in the message).
Environment
- Codex Desktop (macOS):
26.825.51511(build7377) - OS: macOS 14.6.1, Apple Silicon (arm64)
- Provider config:
model_provider = "kimi"
model = "k3"
[model_providers.kimi]
name = "Kimi"
base_url = "https://api.kimi.com/coding/v1"
env_key = "[REDACTED]"
wire_api = "responses"
Normal interactive turns (user-typed messages, normal tool calls) through this same provider work fine.
Steps to reproduce
- Configure the Kimi provider as above.
- Do any of the following:
- Create a heartbeat automation attached to a thread and wait for it to fire;
- Create a cron automation and wait for it to fire;
- From a working thread, call
create_threadorsend_message_to_thread.
- The new turn fails within ~5 seconds with
tool_call_id is not found. The model never receives the prompt.
Evidence
Rollout files (~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl) show the injected item has keys [id, internal_chat_message_metadata_passthrough, name, namespace, output, type] — no call_id:
{"type": "function_call_output", "id": "fco_01a07c84-bb4e-7ce2-b834-a3aa4dc6d3e9", "name": "automation_update", "namespace": "codex_app", "output": "<heartbeat>..."}
{"type": "function_call_output", "id": "fco_01a07c76-1120-73e3-8ed5-3b7acf8f35ce", "name": "create_thread", "namespace": "codex_app", "output": "<codex_delegation>..."}
In one affected thread, 150 function_call_output records were inspected: 149 normal tool outputs carry a call_id; exactly 1 — the heartbeat-injected automation_update — lacks it. That single record poisoned the whole thread: a heartbeat run at 09:31 failed, and two subsequent plain user messages in the same thread failed with the identical error.
Affected thread/session IDs (2026-09-07, Asia/Shanghai):
- Heartbeat run: thread
01a0746b-0025-77b0-aad5-aeb71f25b5ad, turn started 09:31, failed in ~4.7s - Delegated thread via
create_thread:01a07c76-1005-7973-965f-6dfcab2bb6a1, failed in ~6.9s - Cron automation run: thread
01a07c84-b8a5-7d60-aa53-5e8f2bedc9b4, failed in ~9.5s - Delegated message via
send_message_to_thread: sametool_call_iderror
Local workaround (confirms root cause)
Manually editing the rollout JSONL to convert the orphaned function_call_output into a plain user message item un-bricks the thread. This isolates the failure to the injection/serialization layer — scheduler, credentials, network, and prompt content are all unrelated.
Expected behavior
Standalone system-injected function_call_output items should never be sent to strict Responses providers without a valid call_id. Either synthesize a paired function_call + call_id, or normalize the item into a user/developer message at the provider boundary (as proposed in #42376). Additionally, a failed injection should not leave a malformed item in the persisted history that bricks all future turns of the thread.
Related
- #41690 — same root cause, DeepSeek, cron automations
- #41799 — same root cause, Azure, cron automations on macOS
- #42376 — same root cause with a proposed fix (normalize at the non-OpenAI provider boundary)
- #42088 — dangling
function_call_outputbreaking resumed threads
No API keys or credentials are included in this report.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing system-injected function_call_output items through the provider boundary and into the persisted ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl history. Compare the proposed normalization in #42376 and reproduce with the heartbeat, cron, create_thread, or send_message_to_thread paths. Done means strict Responses providers no longer receive orphaned outputs and a failed injection does not permanently poison subsequent turns.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100