Codex Exec Exits 0 but No Image? Verify the Artifacts

Codex Exec Exits 0 but No Image? Verify the Artifacts

Aident AI

Two solid coral and cyan forms sit before a bright threshold while two violet forms dissolve beyond it beneath a detached completion pulse.

Codex Exec Exits 0 but No Image? Verify the Artifacts

If codex exec exits with status 0 but the images you requested are missing, treat the run as incomplete. Exit 0 means the Codex process finished without a process-level failure. It does not prove that every requested side effect happened or that every output path exists.

Preserve any partial images, record the JSONL event stream, and verify the exact files before the next pipeline step. Rerun only the missing work after you understand the failure. Do not delete partial output or repeatedly launch the entire batch.

Add an Artifact Postcondition Now

For a batch that should create six images, make the expected paths explicit and fail the job when any file is absent or empty:

#!/usr/bin/env bash
set -euo pipefail

expected=(
  "images/01.png"
  "images/02.png"
  "images/03.png"
  "images/04.png"
  "images/05.png"
  "images/06.png"
)

missing=()
for path in "${expected[@]}"; do
  [[ -s "$path" ]] || missing+=("$path")
done

if ((${#missing[@]})); then
  printf 'Missing or empty artifact: %s\n' "${missing[@]}" >&2
  exit 1
fi

Run this check immediately after Codex and before an uploader, renderer, deployment, or downstream agent consumes the output. A successful final message is useful context, but the filesystem is authoritative for a filesystem deliverable.

Capture Codex Events Without Hiding Its Exit Status

OpenAI's current non-interactive documentation recommends codex exec for scripts and CI. The --json flag changes the event stream to JSON Lines, with events such as turn.completed, turn.failed, and error.

In Bash, preserve both that stream and the real Codex exit status:

set -o pipefail

codex exec \
  --json \
  --sandbox workspace-write \
  "Create the six requested images at the exact paths in the prompt." \
  | tee codex-events.jsonl

codex_status=${PIPESTATUS[0]}
if ((codex_status != 0)); then
  exit "$codex_status"
fi

if jq -e 'select(.type == "turn.failed" or .type == "error")' \
  codex-events.jsonl >/dev/null; then
  echo "Codex reported a failed turn or error event." >&2
  exit 1
fi

Then run the artifact postcondition. These checks answer different questions:

  • The process status tells you whether the command itself failed.

  • The JSONL stream tells you whether Codex reported a failed turn or error.

  • The artifact check tells you whether the requested deliverables actually exist.

None of the first two replaces the third.

Confirm That This Is the Same Failure

This pattern has a narrow signature:

  1. The prompt names multiple required images or exact output paths.

  2. Codex begins image generation and may return some generated assets.

  3. Fewer files arrive than requested, or generated images remain outside the requested workspace paths.

  4. The final response implies completion or the command exits 0.

  5. A downstream step discovers that one or more files are missing.

OpenAI Codex issue 37717 reports that pattern on Codex CLI 0.147.0 in non-interactive mode: six image artifacts were requested, two default generated images appeared, none of the requested workspace files existed, and the process returned exit 0. The report also includes a child-manager timeout, but an open issue is not a confirmed root-cause analysis. Use the observable artifact mismatch as your gate instead of assuming every partial generation has the same cause.

Observation

What it proves

Next step

codex exec returns nonzero

The process failed

Preserve events and stderr, then diagnose that failure

JSONL contains turn.failed or error

Codex reported a failed turn or error

Stop the pipeline and inspect the event

Exit 0, but an expected path is absent

Delivery contract failed

Keep partial output and fail the artifact gate

Every expected file exists and is nonempty

Basic delivery succeeded

Validate format and content before use

Files exist at unexpected generated-image paths

Generation and workspace delivery diverged

Copy only after reviewing provenance, then fix the prompt or workflow

Make the Prompt Machine-Checkable

Avoid a loose request such as "make several images." Give the run a finite output contract:

Create exactly six PNG images.

Required paths:
- images/01.png
- images/02.png
- images/03.png
- images/04.png
- images/05.png
- images/06.png

Do not report completion unless every required path exists and is nonempty.
If generation stops early, report the completed paths and the missing paths.
Do not delete or overwrite a completed image during recovery

This makes the intended state unambiguous. It still does not make the model's final statement authoritative. Your wrapper must independently check the paths.

If another system needs a structured summary, --output-schema can constrain the final response and -o can save the last message. Ask for a manifest with completed and missing paths, but treat it as a diagnostic record. A structured claim that a file exists is not proof that the file exists.

Validate More Than Existence

An empty or mislabeled file should not pass. Add checks appropriate to the consumer:

for path in "${expected[@]}"; do
  mime=$(file --brief --mime-type "$path")
  case "$mime" in
    image/png|image/jpeg|image/webp) ;;
    *)
      printf 'Unexpected image type for %s: %s\n' "$path" "$mime" >&2
      exit 1
      ;;
  esac
done

shasum -a 256 "${expected[@]}" > images/SHA256SUMS

Dimensions, aspect ratio, transparency, and semantic review may matter too. Validate only the properties your downstream job actually requires. The goal is a small, deterministic acceptance boundary, not an elaborate second agent.

For a broader pattern that applies to code changes as well as images, see How to Verify AI Coding Agent Tests Actually Pass. If a long generated file was truncated instead of omitted, use the recovery steps in How to Recover an AI-Generated 10,000-Line File.

Recover a Partial Batch Without Making It Worse

When two of six images exist, preserve those two and calculate the missing set. Before retrying, save:

  • the Codex version and operating system;

  • the original prompt and required paths;

  • codex-events.jsonl and sanitized stderr;

  • the paths, sizes, and hashes of completed artifacts;

  • the timestamp and any timeout or failed-turn event.

Then submit a bounded repair request that names only the missing paths. Tell Codex not to overwrite existing files. Recheck the complete six-file contract after the repair.

Avoid broad retry loops. Image generation can consume time or credits, and a blind retry can overwrite good output while reproducing the same partial failure. One evidence-backed retry is safer than repeatedly treating exit 0 as success.

Use Loadout for a Bounded Image Delivery Check

Aident Loadout can find the current image-generation options, validate their inputs, show the live price before execution, and keep provider credentials out of the prompt.

Start with this exact setup phrase:

Follow https://aident.ai/SETUP.md

Then ask:

Find a current image-generation Action. Inspect its schema and preflight one bounded 3:2 image request. Show me the exact credit estimate and wait for approval if execution costs credits. After approval, execute only that one image and verify that the response contains a downloadable image asset before reporting success. Do not publish, run a batch, expose credentials, or bypass the integration layer.

The acceptance condition is explicit: one valid input, one approved execution when required, and one downloadable image asset. Current catalog metadata also warns that some image-generation requests can return text without an image, which is another reason to validate the returned artifact rather than trusting a successful request alone.

Set up Aident Loadout and verify one image deliverable.

Sources

Refresh this guide when OpenAI closes issue 37717 with a released fix, changes codex exec event or exit semantics, or adds a first-class artifact delivery contract.

Home

Home

Home

Integrations

Integrations

Integrations

Vault

Vault

Vault

Audit

Audit

Audit

Arana Grande

Arana Grande

Arana Grande

Free

Free

Free

30-day audit summary

30-day audit summary

30-day audit summary

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

View Audit

View Audit

View Audit

Loadout usage

Loadout usage

Loadout usage

617 action calls in the last 30 days

617 action calls in the last 30 days

617 action calls in the last 30 days

May 19 - Jun 17

May 19 - Jun 17

May 19 - Jun 17

10 active days

10 active days

10 active days

Less

Less

Less

More

More

More

Recent activity

Recent activity

Recent activity

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Shopify

Shopify

Shopify

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Success

Success

Success

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Success

Success

Success

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Failed

Failed

Failed

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Connected integration coverage

Connected integration coverage

Connected integration coverage

162

162

162

of 753 accessible connected

of 753 accessible connected

of 753 accessible connected

Callable actions

Callable actions

Callable actions

1,126

1,126

1,126

Vault credentials

Vault credentials

Vault credentials

8

8

8

Explore what's possible

Explore what's possible

Explore what's possible

See all Integrations

See all Integrations

See all Integrations

Google Ads

Google Ads

Google Ads

All available Goolge Ads tools via...

All available Goolge Ads tools via...

All available Goolge Ads tools via...

X (twitter)

X (twitter)

X (twitter)

All available X tools via...

All available X tools via...

All available X tools via...

Github

Github

Github

All available Github tools via...

All available Github tools via...

All available Github tools via...

Notion

Notion

Notion

All available Notion tools via...

All available Notion tools via...

All available Notion tools via...

Slack

Slack

Slack

All available Slack tools via...

All available Slack tools via...

All available Slack tools via...

Firecrawl

Firecrawl

Firecrawl

All available Firecrawl tools via...

All available Firecrawl tools via...

All available Firecrawl tools via...

753 integrations are available for loadouts.

753 integrations are available for loadouts.

753 integrations are available for loadouts.

The one tool

for every tool

your agent needs.

Give any AI agent real capabilities in seconds. Connect 1,000+ tools once, skip the setup headache, and let your agents execute.

Try Aident Loadout

Give your Agent real capabilities in minutes. Connect 1,000+ tools, and let your agents execute.

Try Aident Loadout

Give your Agent real capabilities in minutes. Connect 1,000+ tools, and let your agents execute.

Try Aident Loadout

Give your Agent real capabilities in minutes. Connect 1,000+ tools, and let your agents execute.