codex exec: stdin, JSONL, Sandboxes and Exit Codes Tested
Go deeper on this topic
SaaS integrations
Give AI the context to continue the work: source conversations, open tasks, and unresolved details, from adoption decisions to integration code.
Article 7 of 12 in this series.
Next reads
Dub conversion tracking: lead and sale APIs with Stripe customer mapping
Connect Dub clicks to signups and sales. Check Business requirements, lead and sale fields, USD/JPY amounts, invoiceId, and Stripe metadata with official sources and a credential-free JSON planner.
Dub webhook signature verification: raw bodies and replays in Node.js
Verify Dub-Signature over the received body. A Node.js/Bun Request experiment tests 36 body/signature pairs, second-read TypeErrors, and a UTF-8 BOM limit before a minimal AI handoff.
Record Slack huddles and feed AI at 22:00: six Circleback settings
A Circleback workflow for giving AI usable work context: Slack huddles, a 22:00 sync, source statements, open tasks, and adoption criteria. Separates workflow experience, documented features, and local test evidence.
Use the topic hub as a map, then continue to the next article in the series. New here? Start at the home hub →
codex exec runs OpenAI Codex CLI without its interactive terminal interface. It is the entry point corresponding to Claude Code's claude -p when you want to pass logs, save an answer, or delegate a small task to another agent. In normal output mode, the final answer goes to stdout and progress goes to stderr.
Checked: 2026-10-29. Tested version: codex-cli 0.163.0, macOS. The comparison uses Claude Code 2.1.308 help. Official web documentation was fetched on the same day; it is not a guarantee for every CLI version.
Key points: Start with
-s read-onlyto check inputs and outputs. Useworkspace-writeexplicitly when edits are needed, then inspect the diff and run the tests before handing the work on. A successful CLI exit alone does not establish task success.
What is codex exec?
Pass a task as an argument and Codex works on it, then exits. Run it inside a Git repository. A flag also permits running outside Git, but we did not test that path. It can reuse existing CLI authentication. These probes used an already signed-in Mac; we did not perform a new login or configure an API key. Official non-interactive guide
codex --version
codex exec --help
codex exec -s read-only 'Explain this repository. Do not change files.'In 0.163.0 help, -p selects a profile. Do not use codex -p as the print-mode switch. Use codex exec as the non-interactive entry point when translating a workflow from claude -p. Include the CLI help version and execution settings in the handoff. Instruction file discovery
How does it compare with claude -p?
This table compares official documentation fetched on 2026-10-29 and help from both installed CLIs. It is not a comparative experiment of performance or safety. Codex reference, Claude Code CLI reference, Claude Code headless guide
| Goal | Codex CLI 0.163.0 | Claude Code 2.1.308 help and official docs |
|---|---|---|
| Run without an interactive interface | codex exec 'task' |
claude -p 'task' |
| Supply logs through stdin | Combine piped input with a prompt argument | Combine piped input with claude -p |
| Receive streaming events | --json produces JSONL |
--output-format stream-json |
| Specify the final response shape | --output-schema schema.json |
--output-format json --json-schema '<schema>' |
| Resume a conversation | codex exec resume <SESSION_ID> 'task' |
claude -p --resume <SESSION_ID> 'task' |
| Control permitted operations | --sandbox and approval configuration |
--permission-mode and related controls; not equivalents of Codex sandbox values |
For moving work between Claude Code conversations, see context handoff.
How do I pass logs through stdin?
Keep the instruction separate from the piped log. The 0.163.0 help says piped input is appended as a <stdin> block when a prompt argument is also supplied.
printf '%s\n' 'ERROR E_DEMO: missing fixture' |
codex exec -s read-only \
'Explain likely causes and the next check. Do not run commands or edit files.' \
> answer.txt 2> progress.txtUse codex exec - to read the entire instruction from stdin. The input goes to the model, so remove secrets and personal data from real logs. In the synthetic probe, Codex read the E_DEMO marker and returned the requested LOG_OK.
Normal stdout contains the final answer in answer.txt; stderr contains progress and diagnostics in progress.txt. Our stderr also included startup information and an authentication warning from an MCP server the task did not use. Text on stderr alone does not establish task failure: inspect the exit code and the diagnostic.
How do I get JSON output? Is JSONL the final answer?
--json emits JSONL, with one event per line. Do not parse all stdout as a single JSON object. Use --output-last-message, or -o, to save just the final answer separately. Official output behavior
codex exec -s read-only --json \
--output-last-message answer.txt \
'Briefly explain this repository. Do not change it.' \
> events.jsonl 2> progress.txtThe workspace-write probe produced seven JSONL lines, including thread.started, turn.started, item.started, item.completed, and turn.completed. Do not assume every task has that line count or event order. In normal output mode, -o saved the same final answer as stdout after excluding the trailing newline.
Try the following in an empty synthetic Git project. Existing output files will be overwritten. To specify final response fields, provide a JSON Schema file. This is a syntax example: ok: true does not prove that a task was performed correctly.
cat > schema.json <<'JSON'
{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}
JSON
codex exec -s read-only --json --output-schema schema.json \
-o answer.json 'No tools. Return ok true.' \
> events.jsonl 2> progress.txtThe synthetic schema probe saved {"ok":true} to answer.json, matching the final message in the JSONL stream. Downstream code should still parse the JSON, validate required fields, and inspect the actual artifacts.
How do I use read-only? How is it different from approvals?
The sandbox controls command permissions. Approval configuration determines how operations that require confirmation are handled. Making both explicit helps the next operator understand the conditions. The never policy does not ask the user for approval. We did not test how operations requiring approval are handled. It does not establish the safety of the whole task; try these examples in an independent working checkout.
codex exec -s read-only -c 'approval_policy="never"' \
'Read git diff and report possible issues with line references. Do not edit.'
codex exec -s workspace-write -c 'approval_policy="never"' \
'Make only the specified small fix. Report tests run and remaining work.'The official non-interactive guide describes read-only as the default. We explicitly selected modes in the probes. Other permission options in the 0.163.0 help, including automatic approval review, were not tested.
We asked each mode to run printf PROBE > marker.txt once in a synthetic Git project. In read-only, no file appeared and the model reported a write denial. However, that JSONL stream contained no shell execution event, so we could not independently verify the shell exit code of 1 reported by the model. In workspace-write, a shell event reported exit code 0 and the actual file contained PROBE.
What do the exit codes mean?
Distinguish the CLI process exit code from the exit code of a command it runs. The read-only probe exited 0 even though it did not create the file. A parent agent needs to inspect diffs, artifacts, and tests alongside the final answer.
| Case run on 0.163.0 | CLI exit code | Independently observed result |
|---|---|---|
| Synthetic log supplied through stdin | 0 | stdout was LOG_OK, matching -o except for the trailing newline |
| Write requested in read-only | 0 | No file; no shell event recorded |
| Same write requested in workspace-write | 0 | File contained PROBE; shell event exit was 0 |
| Resume this experiment's session by ID | 0 | Returned the conversation marker TEAL42 |
| Final answer constrained by schema | 0 | Saved {"ok":true} |
| Invalid sandbox value | 2 | Rejected during argument validation |
| Resume a fixed nonexistent ID | 1 | Session-not-found error |
These are seven measured cases, not an exhaustive exit-code specification for expired authentication, connection failures, limits, timeouts, or signals. Measured summary JSON
When piping test output, preserve the original test's status separately. A successful pipeline or Codex exit does not establish that the original tests passed. How exit codes get lost in hooks
How do I resume earlier work?
codex exec -s read-only resume <SESSION_ID> \
'List the previous open questions. No tools. Preserve undecided items.'Replace <SESSION_ID> with the actual ID. A parent process can save thread_id from the thread.started JSONL event. Resuming the workspace-write probe by that ID returned TEAL42, a marker present only in the earlier instruction. This was one conversation-continuity check without rereading a file; complex handoff quality was not tested.
Help also documents codex exec resume --last 'task'. An explicit ID may be preferable when other jobs may run concurrently and the parent needs to record which conversation it continues. We did not test --last.
How should another agent delegate a small task?
Have the parent define scope, permissions, and acceptance criteria, then delegate one task in an independent Git checkout or similar workspace. This is an operating recommendation, not a tested automatic merge workflow.
- Name the files, reason for the change, expected behavior, required tests, and excluded scope.
- Use read-only for investigation or workspace-write for necessary edits; remove sensitive log data first.
- Compare the CLI exit code, JSONL, and final message with
git diff --checkandgit diff. - Have the parent run the required tests too, then hand on verified results and untested items separately.
Goal and target files:
Expected change and excluded scope:
Target commit, CLI version, sandbox, approval configuration:
Tests that establish completion:
Artifact diff and tests actually passed:
Open decisions, untested scope, next action:Include MCP configuration locations when connections matter, CLAUDE.md and AGENTS.md discovery when instruction scope matters, and usage interpretation when recording what usage figures cover.
What was measured, and what remains untested?
Five model-backed cases and two error cases—invalid arguments and a nonexistent session ID—ran once each in a disposable Git project. The probes passed --ignore-user-config and -c project_doc_max_bytes=0 to skip config.toml loading and set the document-byte option. We did not independently check whether instruction documents were actually excluded. These flags do not disable every MCP server or plugin; MCP startup warnings were still observed. No model or reasoning override argument was supplied. The initial banner showed model gpt-6.1-sol and reasoning effort none. We also inspected the official source at the matching tag.
We did not directly edit user settings. However, before/after SHA-256 checks matched for AGENTS.md and differed for config.toml. The relationship to CLI activity or concurrent activity is unknown, so this experiment does not establish settings immutability. Session records were also generated. Do not interpret read-only as a prohibition on writes to every CLI storage location.
Linux, Windows, network and outside-workspace access, automatic approval review, expired authentication, limits, interruption, --last, and other CLI versions remain untested. Output depends on input and configuration. Check help, permissions, and actual results in your environment before handing over work.
FAQ
- Q. Is codex -p the non-interactive command?
- Use codex exec on 0.163.0. The -p flag selects a profile. When translating a claude -p workflow, check the entry point, output format, and permission settings separately.
- Q. Can I pipe test logs into codex exec?
- Yes. Supply the instruction as an argument and pipe the log through stdin as additional context. Normal final output goes to stdout; progress and diagnostics go to stderr. Preserve the original test status separately.
- Q. Does codex exec --json return one JSON document?
- It emits JSONL, one event per line. Use --output-schema to constrain the final response and --output-last-message to save it separately. Our schema probe saved ok:true.
- Q. Does requesting a write in read-only give a nonzero CLI exit?
- Our 0.163.0 probe created no file but exited 0. No shell event was recorded, so the shell status reported by the model was not independently verified. Inspect artifacts, diffs, and tests too.
- Q. Can I continue an earlier codex exec conversation?
- Pass an explicit session ID to codex exec resume. Our resumed probe recalled a marker from its previous instruction. Help also documents --last, which we did not test.