openai / openai/codex

Automations (heartbeat & cron) inject a `function_call_output` without `call_id`, permanently breaking the target session (400 missing field call_id)

Open
#44,723 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app automations bug custom-model session
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Component: Codex desktop app (Windows), package 26.903.9818.0 (ChatGPT.exe 152.0.7977.83), bundled codex-cli 0.153.4
Model provider: custom OpenAI-compatible provider in config.toml with wire_api = "responses" (base_url https://api.deepseek.com/)

Summary

When an automation fires (heartbeat or cron), the app appends a synthetic tool-result item to the target session and posts the session history to the Responses endpoint. The item is a function_call_output for the app tool automation_update, but it carries no call_id, so the server rejects the entire request:

Failed to deserialize the JSON body into the target type:
input: missing field `call_id` at line 1 column 851416

Because the item stays in the session history, the session is permanently bricked: every later request fails the same way, including ordinary user messages. Each failed automation run appends yet another bad item. In our case the automation never ran successfully even once, and the failure was silent.

Steps to reproduce

  1. Configure a custom Responses-API provider (wire_api = "responses") in config.toml.

  2. Create a heartbeat automation targeting an existing thread (reproduced), or a cron automation bound to a project (also reproduced).

  3. Wait for the first trigger.

  4. The run ends immediately with the 400 above.

  5. In the session rollout JSONL, the injected trigger item looks like this (no call_id; note the non-standard name/namespace fields and the fco_ id prefix):

    {"type":"function_call_output",
    "id":"fco_01a08cf7-e93a-7641-98bf-75581dc6ad04",
    "name":"automation_update",
    "namespace":"codex_app",
    "output":"\r\n <automation_id>automation</automation_id>\r\n <current_time_iso>2026-09-10T20:17:32.150Z</current_time_iso>\r\n...",
    "internal_chat_message_metadata_passthrough":{"turn_id":"01a08cf7-...","create_time":1789071452.75}}

  6. Send any ordinary message in that same thread: it also fails with the same error.

Evidence

  • Ordinary turns in another thread work fine against the same provider, so the request path itself is healthy - only the automation-delivered item is malformed.
  • All automation failures show the same message with a monotonically growing column, because the session keeps growing (~3 KB per failed run): 839237, 842598, 845959, 849320, 851416.
  • A cron automation reproduced it in a brand-new thread too (column 46888), so this is not heartbeat-specific.
  • Per the Responses API schema, function_call_output items require call_id (see https://developers.openai.com/api/docs/guides/function-calling).

Expected

Automation triggers should be delivered as schema-valid input - e.g. a function_call_output that carries the call_id of the corresponding automation_update function call, or simply as a normal user message. A failed run should not permanently damage the session.

Suggested fix

  1. Populate call_id on the injected item (pair it with the automation's automation_update call), or deliver the trigger as a regular message.
  2. Validate outgoing input items against the Responses schema before sending, and skip/log invalid synthetic items instead of letting the server reject the whole turn.
  3. Make sessions self-healing: do not persist an item the server rejected, or provide a way to drop/repair invalid items from a broken thread.

Impact

  • Scheduled automations are unusable (5 failures / 0 successful runs).
  • The target conversation is permanently broken and cannot be repaired from the UI.
  • Silent monitoring failure: our automation was supposed to watch a live trading loop every 2 hours; it never ran, and nobody noticed until we investigated manually.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing heartbeat and cron automation triggers into the Responses endpoint request, using config.toml and the session rollout JSONL example to locate serialization of the synthetic function_call_output item. Compare the generated item with the Responses function-calling schema and verify that failed requests do not leave invalid items that break 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
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.