codex exec --output-schema does not apply only to final output
What version of Codex CLI is running?
v0.125.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.5
What platform is your computer?
Linux
What terminal emulator and version are you using (if applicable)?
_No response_
What issue are you seeing?
codex exec --output-schema does not appear to apply only to the final output.
When an agent run emits intermediate progress messages before tool calls, those intermediate agent_message items are also forced into the provided output schema. This can break workflows that consume the first schema-valid JSON message as the final result, because intermediate messages can look like valid final outputs.
In my case, this means automated workflows using --json --output-schema can intermittently fail or consume the wrong result depending on whether the model emits progress messages during the run.
What steps can reproduce the bug?
Create schema.json:
{
"type": "object",
"properties": {
"final": {
"type": "string"
}
},
"required": [
"final"
],
"additionalProperties": false
}
Run:
codex exec --json --output-schema schema.json \
"Strictly follow these three steps: First, say in one sentence that you will run pwd, then run pwd. Second, say in one sentence that you will run ls -1 | head -5, then run ls -1 | head -5. Third, say in one sentence that you will run git status --short, then run git status --short. Finally, summarize the results of all three commands. You must output the one-sentence explanation before running each command."
Actual output includes intermediate messages that already satisfy the final schema:
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"{\"final\": \"I will run `pwd` to confirm the current working directory.\"}"}}
{"type":"item.completed","item":{"id":"item_2","type":"agent_message","text":"{\"final\":\"I will run `ls -1 | head -5` to list the first five directory entries.\"}"}}
{"type":"item.completed","item":{"id":"item_4","type":"agent_message","text":"{\"final\":\"I will run `git status --short` to check the concise worktree status.\"}"}}
Only the last message is actually the final result:
{"type":"item.completed","item":{"id":"item_6","type":"agent_message","text":"{\"final\":\"Results: `pwd` returned `/home/sunday/Projects/codex`. `ls -1 | head -5` returned `AGENTS.md`, `BUILD.bazel`, `CHANGELOG.md`, `LICENSE`, and `MODULE.bazel`. `git status --short` shows one untracked file: `schema.json`.\"}"}}
What is the expected behavior?
--output-schema should constrain only the final output of codex exec, not intermediate progress messages in the agent loop.
Intermediate agent_message items should remain normal progress messages, and only the final returned result should be schema-compliant.
Additional information
From a quick source check, final_output_json_schema appears to be copied into each prompt built during the sampling loop, so every Responses API request in the turn may include text.format.schema.
Related issue: #15451 discusses schema/tool behavior, but this report is specifically about intermediate agent messages being shaped as final schema output.
2 Comments
This is a limitation of the current responses server endpoint. There's unfortunately nothing we can do to address this in the Codex harness / client.
I'll leave the bug report open, but I want to set expectations that it's unlikely this will be fixed any time soon, if ever.
Could Codex avoid this by modeling structured final output as a tool call instead of text.format?
For example, when
--output-schemais set, the harness could expose a syntheticfinal_answertool whose parameters are the provided schema, treat calling that tool as task completion, and serialize its arguments as the final output. Interim assistant messages would remain unconstrained/plain text, while the final result would still be schema-validated.That seems like it would avoid needing Responses to support “schema only for the final assistant message,” since the schema would live on a tool definition rather than on all assistant text. Is there an API/server limitation that prevents this approach too, or is the concern mostly that it would be a larger harness-level contract change?