codex exec review silently ignores --output-schema and writes prose
What version of Codex CLI is running?
codex-cli 0.145.0
What subscription do you have?
Plus
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Darwin 25.5.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
cmux, backed by Ghostty 1.3.2
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1785149796s since unix epoch",
"overallStatus": "warning",
"codexVersion": "0.145.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/Users/ajaysingh/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/Users/ajaysingh/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/Users/ajaysingh/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/Users/ajaysingh/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/Users/ajaysingh/.codex/app-server-daemon/app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "/Users/ajaysingh/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/Users/ajaysingh/.codex",
"config.toml": "/Users/ajaysingh/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/Users/ajaysingh/Desktop/workflow",
"enabled feature flags": "shell_tool, unified_exec, shell_snapshot, code_mode_host, terminal_resize_reflow, sqlite, memories, hooks, enable_request_compression, network_proxy, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, browser_use, browser_use_full_cdp_access, browser_use_external, computer_use, remote_plugin, plugin_sharing, image_generation, resize_all_images, skill_mcp_dependency_install, skill_search, mentions_v2, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, auth_elicitation, personality, fast_mode, tui_app_server, prevent_idle_sleep, remote_compaction_v2, workspace_dependencies",
"feature flag overrides": "memories=true, network_proxy=true, prevent_idle_sleep=true",
"feature flags enabled": "39",
"log dir": "/Users/ajaysingh/.codex/log",
"mcp servers": "0",
"model": "gpt-5.6-sol",
"model provider": "openai",
"sqlite home": "/Users/ajaysingh/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.47.0",
"details": {
".git entry": "directory",
"PATH git #1": "/opt/homebrew/bin/git",
"PATH git #2": "/usr/bin/git",
"PATH git entries": "2",
"git branch": "main",
"git build options": "git version 2.47.0; cpu: arm64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh; feature: fsmonitor--daemon; libcurl: 8.7.1; zlib: 1.2.12",
"git exec path": "/opt/homebrew/opt/git/libexec/git-core",
"git version": "git version 2.47.0",
"repo detected": "true",
"repo root": "/Users/ajaysingh/Desktop/workflow",
"selected git": "/opt/homebrew/bin/git"
},
"remediation": null,
"durationMs": 170
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/var/folders/7n/wjq8k35d11d1sfqfzycnj4z00000gn/T//cmux-cli-shims/D50C9855-34A6-470D-96AB-AB3DFE14E983/codex",
"PATH codex #2": "/Users/ajaysingh/.local/bin/codex",
"PATH codex #3": "/var/folders/7n/wjq8k35d11d1sfqfzycnj4z00000gn/T/cmux-cli-shims/D50C9855-34A6-470D-96AB-AB3DFE14E983/codex",
"PATH codex entries": "3",
"current executable": "/Users/ajaysingh/.local/bin/codex",
"install context": "standalone (unix, package /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin, bin /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin, resources /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/codex-resources, path /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/codex-path)",
"managed by bun": "false",
"managed by npm": "false",
"managed by pnpm": "false",
"managed package root": "not set"
},
"remediation": null,
"durationMs": 12
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "no MCP servers configured",
"details": {},
"remediation": null,
"durationMs": 0
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars": "none"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 404)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 478
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "2 IPv4, 2 IPv6, first IPv4",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"models etag present": "true",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 1002
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on macos-aarch64",
"details": {
"commit": "unknown",
"current executable": "/Users/ajaysingh/.local/bin/codex",
"install method": "standalone (unix, package /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin, bin /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin, resources /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/codex-resources, path /Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/codex-path)",
"platform": "macos-aarch64",
"version": "0.145.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "/Users/ajaysingh/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/codex-path/rg",
"search command readiness": "file exists",
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "OnRequest",
"codex-linux-sandbox helper": "none",
"execve wrapper helper": "/Users/ajaysingh/.codex/tmp/arg0/codex-arg0SGlMhg/codex-execve-wrapper",
"filesystem sandbox": "restricted",
"network sandbox": "restricted"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/Users/ajaysingh/.codex (dir)",
"active rollout files": "60 files, 26219244 total bytes, 436987 average bytes",
"archived rollout files": "0 files, 0 total bytes, 0 average bytes",
"goals DB": "/Users/ajaysingh/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "/Users/ajaysingh/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/Users/ajaysingh/.codex/log (missing)",
"memories DB": "/Users/ajaysingh/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"sqlite home": "/Users/ajaysingh/.codex (dir)",
"standalone release cache": "2 entries in /Users/ajaysingh/.codex/packages/standalone/releases",
"state DB": "/Users/ajaysingh/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "/Users/ajaysingh/.codex/thread_history_1.sqlite (missing)",
"thread history DB integrity": "skipped (missing)"
},
"remediation": null,
"durationMs": 236
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "ok",
"summary": "rollout files and state DB thread inventory agree",
"details": {
"default model provider": "openai",
"rollout DB active files": "60",
"rollout DB active rows": "60",
"rollout DB archive mismatches": "0",
"rollout DB archived files": "0",
"rollout DB archived rows": "0",
"rollout DB duplicate DB paths": "0",
"rollout DB duplicate rollout thread ids": "0",
"rollout DB malformed file names": "0",
"rollout DB missing active rows": "0",
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=60",
"rollout DB rows": "60",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "exec=38, cli=21, subagent:review=1",
"rollout DB stale rows": "0"
},
"remediation": null,
"durationMs": 232
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language en-IN",
"details": {
"EDITOR": "not set",
"LANG": "en_IN.UTF-8",
"VISUAL": "not set",
"os": "Mac OS 26.5.2 [64-bit]",
"os language": "en-IN",
"os type": "Mac OS",
"os version": "26.5.2"
},
"remediation": null,
"durationMs": 4
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"TERM_PROGRAM": "ghostty",
"color output": "enabled",
"effective locale": "en_IN.UTF-8",
"stderr is terminal": "true",
"stdin is terminal": "true",
"stdout is terminal": "true",
"terminal": "Ghostty",
"terminal size": "215x61",
"terminal version": "1.3.2-HEAD-+eb500e9"
},
"remediation": null,
"durationMs": 0
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"terminal title activity": "true",
"terminal title items": "activity, project-name",
"terminal title project source": "git repo root",
"terminal title project value": "workflow",
"terminal title source": "default"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "warning",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.145.0",
"check for update on startup": "true",
"last checked at": "2026-07-26T15:32:26.240316Z",
"latest version probe": "curl: (56) The requested URL returned error: 403",
"update action": "standalone installer",
"version cache": "/Users/ajaysingh/.codex/version.json"
},
"remediation": null,
"durationMs": 171
}
}
}
What issue are you seeing?
codex exec review accepts the global --output-schema option and exits with code 0, but the schema is not applied to the final response.
The file passed to --output-last-message contains human-readable dedicated-review prose rather than JSON conforming to the supplied schema.
The same schema works correctly when used with ordinary codex exec.
Observed behaviour:
- Ordinary
codex exec --output-schema ...writes schema-conforming JSON. codex exec --output-schema ... review -writes review prose.- Moving
--output-schemabefore or after thereviewsubcommand does not change the result. - Removing
--jsondoes not change the result. - With
--json, the event stream contains anagent_messagewith prose rather than structured review output. - The Codex process exits with code 0 even though the requested output contract was not honoured.
This makes it unsafe to use dedicated review mode in automated workflows because a caller cannot reliably distinguish a valid structured verdict from unstructured review prose.
What steps can reproduce the bug?
Create a disposable Git repository:
REPRO_DIR="$(mktemp -d)"
cd "$REPRO_DIR"
git init
git config user.name "Codex Reproduction"
git config user.email "codex-repro@example.com"
printf 'original\n' > example.txt
git add example.txt
git commit -m "Initial commit"
printf 'changed\n' >> example.txt
Create a minimal output schema:
cat > review-schema.json <<'JSON'
{
"type": "object",
"properties": {
"verdict": {
"type": "string",
"enum": ["correct", "incorrect"]
},
"explanation": {
"type": "string"
}
},
"required": [
"verdict",
"explanation"
],
"additionalProperties": false
}
JSON
Create a prompt:
cat > prompt.txt <<'PROMPT'
Review the current uncommitted change.
Return a result matching the supplied output schema.
PROMPT
First run ordinary codex exec:
codex exec \
--sandbox read-only \
--cd "$REPRO_DIR" \
--ephemeral \
--color never \
--output-schema "$REPRO_DIR/review-schema.json" \
--output-last-message "$REPRO_DIR/ordinary-output.json" \
- < "$REPRO_DIR/prompt.txt"
The resulting ordinary-output.json is valid JSON matching the schema.
Then run dedicated review mode with the same schema:
codex exec \
--sandbox read-only \
--cd "$REPRO_DIR" \
--ephemeral \
--color never \
--output-schema "$REPRO_DIR/review-schema.json" \
--output-last-message "$REPRO_DIR/dedicated-review-output.json" \
review - < "$REPRO_DIR/prompt.txt"
The second command exits successfully, but dedicated-review-output.json contains human-readable review prose rather than schema-conforming JSON.
It can be verified with:
python3 -m json.tool "$REPRO_DIR/ordinary-output.json"
python3 -m json.tool "$REPRO_DIR/dedicated-review-output.json"
The ordinary output parses successfully, while the dedicated-review output does not.
The behaviour is also reproducible with JSONL diagnostics enabled:
codex exec \
--sandbox read-only \
--cd "$REPRO_DIR" \
--ephemeral \
--color never \
--output-schema "$REPRO_DIR/review-schema.json" \
--output-last-message "$REPRO_DIR/dedicated-review-output.json" \
--json \
review - < "$REPRO_DIR/prompt.txt" \
> "$REPRO_DIR/events.jsonl"
The JSONL stream contains the review as an unstructured agent_message.
What is the expected behavior?
One of the following should happen:
codex exec reviewhonours--output-schemaand writes schema-conforming JSON to the path supplied through--output-last-message; or- If structured output is intentionally unsupported by dedicated review mode, the CLI rejects the option combination with a clear nonzero exit code and explanatory error.
The CLI should not silently ignore the schema and exit successfully.
Additional information
This is specific to the dedicated review subcommand.
The same Codex installation, model, repository, prompt, and schema produce valid structured JSON through ordinary codex exec.
Tested variations:
--output-schemabefore thereviewsubcommand--output-schemaafter thereviewsubcommand- with
--json - without
--json
All dedicated-review variations produced prose.
The issue affects automated review pipelines that depend on strict machine-readable approval or rejection. Parsing the prose would weaken correctness and fail-closed guarantees.