Codex Exec Command: JSON Output and Script Checks

October 25, 2026 · By Taylor Yang · #Codex #AI coding

Run the Codex exec command from a script, send input through stdin, save a structured result, inspect JSONL events, and check failures before using it in CI.

Actual structured shipping quote returned by a non-interactive Codex exec test.

The Codex exec command runs a task without opening an interactive conversation screen. Give it a prompt, set the working directory and permissions, then inspect its exit status and saved result. With --json, standard output contains a stream of JSONL events; it is not a single final JSON answer. This tutorial tests a small shipping-rule calculation, saves the answer against a JSON schema, and checks a missing-schema failure. It also verifies input through standard input. Use those checks as a starting point for scripts or CI, where a command that printed something is not necessarily a job that completed correctly.

Prepare a small test repository

Use a valid Codex sign-in and a model available to your account. The CLI guide covers the interactive starting point, while the command reference helps you inspect your installed options.

Create a disposable repository:

mkdir cedar-exec
cd cedar-exec
git init
codex --version
codex exec --help

Our tests used CLI 0.146.0 on macOS and gpt-5.6-sol with low reasoning effort. Use your account’s available model; the recorded version and model explain these results rather than promising identical behavior for every installation.

Save this as quote.schema.json:

{
  "type": "object",
  "properties": {
    "shipping": {"type": "number"},
    "currency": {"type": "string"},
    "eligible": {"type": "boolean"}
  },
  "required": ["shipping", "currency", "eligible"],
  "additionalProperties": false
}

The fictional rule is simple: shipping costs $5 below a $50 subtotal and is free at $50 or more. For a subtotal of exactly $50, the expected answer is zero shipping, USD currency, and eligibility set to true. This boundary value makes an off-by-one interpretation visible.

Actual JSON schema used to require the three shipping-result fields.

The schema specifies shape and types. The separate expected value checks the business rule.

Save events and the final answer separately

Run from the practice folder:

codex exec -s read-only \
  -m gpt-5.6-sol \
  -c 'model_reasoning_effort="low"' \
  --json \
  --output-schema quote.schema.json \
  -o quote.json \
  'Shipping is 5 USD below a 50 USD subtotal and free at 50 or more.
   For subtotal exactly 50, return shipping, currency, and eligible.
   Do not call tools or modify files.' \
  > events.jsonl 2> run.stderr
result=$?
printf 'Process exit: %s\n' "$result"

The shell writes the output files; the agent is not being asked to create them. Our actual test used the same schema and rule, supplied the rule through standard input alongside an instruction, and disabled unrelated user rules for an isolated fixture. It returned:

{"shipping":0,"currency":"USD","eligible":true}

The process exited successfully. The final response was saved separately from the event stream.

OutputContainsUseful check
events.jsonlOne event object per lineParse each line and inspect completion or failure
quote.jsonFinal responseParse and compare the expected fields
run.stderrDiagnostic outputInspect when startup or execution fails
Process statusShell exit codeTreat a nonzero code as failure

On a phone, swipe sideways to read the last column.

The official non-interactive guide documents these output options. Do not feed the whole JSONL file to a parser expecting one JSON object, or use the last line as though every event type had the same fields.

Check meaning as well as JSON shape

For this known-answer fixture, validate the saved result with Python:

python3 - <<'PY'
import json
from pathlib import Path

result = json.loads(Path("quote.json").read_text())
expected = {"shipping": 0, "currency": "USD", "eligible": True}
assert result == expected, result
print("Shipping boundary check passed")
PY

A valid schema cannot tell you that the shipping rate is correct. That is why this test uses an expected answer independent of the model’s explanation. For a repository edit, replace the fixed answer with appropriate tests, a diff inspection, and checks for unintended file changes.

Our no-tool calculation did not modify a project file or call a shipping provider. It is a demonstration of input, structured output, and validation, not a recommendation to replace deterministic checkout arithmetic with a model call.

Use permissions and approval settings to choose a boundary for real scripts. A non-interactive job cannot rely on someone noticing an unexpected approval prompt. It should fail clearly when its allowed access is insufficient, rather than silently escalating to unrestricted access.

Send a complete prompt through stdin

Use - as the prompt argument when a pipeline supplies the input:

printf '%s\n' \
  'Reply exactly CEDAR_STDIN. Do not call tools or modify files.' | \
  codex exec -s read-only -m gpt-5.6-sol \
    --json -o stdin-result.txt - \
    > stdin-events.jsonl 2> stdin.stderr

Our actual stdin-only test returned CEDAR_STDIN and exited successfully.

Actual saved response from the stdin-only Codex exec test.

The marker confirms that the supplied stdin prompt reached the task.

When composing a pipeline, separate trusted instructions from untrusted document contents. Do not let text from a pull request or downloaded file decide which secrets the process receives or which shell commands your surrounding script executes.

For an existing conversation, use the supported resume workflow described in Codex sessions. Starting another exec invocation without selecting the previous thread is not the same as continuing its context. For programmatic thread management, the SDK guide provides tested examples.

Verify a failure before putting the job in CI

We repeated a run with an output-schema path that did not exist. The CLI exited with code 1 and reported that it could not read the schema. This is a useful integration check because a pipeline must recognize startup failure even when no model result is produced.

Actual missing-schema error and nonzero exit from the test.

A missing local schema failed the command before a valid result could be accepted.

Capture the status immediately after the Codex command. A later cat, echo, or successful cleanup command can replace the shell’s current status. Preserve the failure in the overall job result. Also remove or use a unique path for old output files so a failed new run cannot accidentally reuse yesterday’s answer.

For CI, make the following acceptance checks part of the job:

  1. The required executable, schema, working directory, and authentication are available.
  2. The Codex process exits successfully and the event stream contains no turn failure.
  3. The expected output file was created by this run and parses correctly.
  4. Its contents or resulting repository changes pass an independent acceptance check.
  5. Diagnostics are retained without exposing credentials or private input unnecessarily.

Provide authentication only to the trusted process that needs it. A repository-controlled install script or test command can read inherited environment variables. Follow the official CI authentication guidance for your environment, and avoid exposing credentials to untrusted fork code.

We ran these checks locally, not on a hosted CI runner. Runner setup, secret handling, checkout permissions, and network access still need verification in your own pipeline. Start with the same harmless known-answer test there before authorizing any repository edits or external actions.