codex exec Complete Guide: Non-Interactive Mode, Exit Codes & CI Automation

codex exec is Codex CLI's non-interactive subcommand — no UI, no dialogs, it runs a task and exits with a meaningful exit code. It is the primary way to embed AI capabilities into GitHub Actions, GitLab CI, cron jobs, and shell scripts. This guide covers everything from syntax to production-grade CI integration.

What is codex exec

codex exec is the subcommand introduced in Codex CLI 1.0, designed specifically for automation and scripting. Unlike the interactive mode that opens a full-screen TUI, codex exec accepts a natural-language task description, calls the model in the background to complete the task, then exits with code 0 (success) or non-zero (failure).

Its value boils down to three things:

  • No human intervention needed: No approval popups, no waiting on stdin. It runs unattended in any environment.
  • Reliable exit codes: Scripts and CI systems can read the exit code to decide whether to continue, retry, or fail the pipeline.
  • Composable: Works seamlessly with shell primitives like &&, ||, timeout, and if to build complex automation workflows.
i

Typical use cases: running code style checks on every PR, generating CHANGELOG entries on each push, automated security scanning, test completion, and scheduled batch AI tasks like daily report generation or data processing.

Basic Syntax & All Flags

The full command format for codex exec is:

Basic syntax
codex exec [options] "<task>"

The <task> is a natural-language description of what you want Codex to do, typically wrapped in quotes. All supported flags are listed below:

Flag Short Default Description
--model <name> -m Model from config.toml Specify the model to use, e.g. gpt-4.1-mini, o4-mini
--approval-mode <mode> -a suggest Set approval mode: suggest, auto-edit, or full-auto
--quiet -q false Silent mode — suppress intermediate progress logs, only output final result
--full-auto false Shorthand for --approval-mode full-auto
--dangerously-auto-approve-everything false Disable all approvals and sandbox restrictions. Use only in controlled CI environments
--working-dir <path> Current directory Specify the working directory; defaults to the directory from which the command is run
--config <path> ~/.codex/config.toml Path to a custom configuration file

Some practical examples:

Common command examples
# Simplest form: just provide the task
$ codex exec "Add JSDoc comments to all public methods in src/utils.ts"

# Specify model + quiet mode (CI-friendly)
$ codex exec --model gpt-4.1-mini --quiet \
    "Read git log --oneline -20 and update CHANGELOG.md"

# Full-auto mode (recommended for CI with --quiet)
$ codex exec --full-auto --quiet \
    "Run npm test; if any tests fail, fix the relevant files in src/"

# No restrictions (use with caution in production)
$ codex exec --dangerously-auto-approve-everything \
    "Install dependencies, build the project, and run end-to-end tests"

Interactive Mode vs codex exec

Understanding the fundamental differences between the two modes is essential before using codex exec in any automation context.

Feature Interactive (codex) Non-interactive (codex exec)
Launch Opens a full-screen TUI Accepts args and runs directly, no UI
stdin requirement Requires a TTY / interactive terminal No TTY needed; works in pipes and CI
Approval dialogs Pops up a confirmation for each sensitive action No prompts; fully automated
Exit mechanism User presses q or Ctrl+C Exits automatically when the task finishes
Exit codes N/A (always exits 0) 0=success, 1=task failed, 2=config error
Slash commands Supports /model, /init, etc. Not supported; use CLI flags instead
Multi-turn conversation Yes; you can follow up and correct No; single-shot execution
Best for Daily development, codebase exploration, creative tasks CI/CD, scripts, cron jobs, batch processing
i

A simple rule of thumb: if you need a human in the loop, use interactive mode; if you need a machine in the loop, use codex exec.

Approval Modes Explained

The approval mode (--approval-mode) controls how Codex behaves when it needs to read/write files or execute shell commands. Choosing the right mode is critical in non-interactive exec contexts.

suggest mode (default)

This is the default. Codex generates suggestions and proposed file changes but does not write any files or execute any commands — it only prints its suggestions to stdout. In codex exec, this means you see Codex's analysis and recommendations, but the working directory remains completely unchanged.

suggest mode example (output only, no file changes)
# Default suggest mode: outputs a code review report, modifies nothing
$ codex exec "Review the code in src/ and list potential performance issues"

# Explicit equivalent
$ codex exec --approval-mode suggest \
    "Review the code in src/ and list potential performance issues"

auto-edit mode

Codex can automatically read and write files within the working directory, but running shell commands (such as npm install or git commit) still requires approval. In a CI context where your task only modifies code files without running commands, this is a safe and useful mode.

auto-edit mode example (files auto-edited, commands need approval)
# auto-edit: modifies files automatically; good for formatting, adding comments
$ codex exec --approval-mode auto-edit \
    "Replace all console.log calls in src/ with logger.debug"
!

In non-interactive exec mode, if auto-edit encounters an operation that requires running a shell command, Codex will skip that operation rather than waiting for confirmation. This may result in an incomplete task. If your task needs shell commands, use full-auto instead.

full-auto mode

Codex can automatically perform everything within the sandbox: read and write files, run commands, install dependencies, and more. The sandbox still restricts access to filesystem paths outside the working directory and most network requests (except the OpenAI API). This is the most commonly used mode for CI/CD pipelines.

full-auto mode example (recommended for CI)
# full-auto: fully automated within the sandbox; handles most CI tasks well
$ codex exec --full-auto --quiet \
    "Run npm run lint and fix all auto-fixable ESLint errors"

# Equivalent explicit form
$ codex exec --approval-mode full-auto --quiet \
    "Run npm run lint and fix all auto-fixable ESLint errors"

dangerously-auto-approve-everything mode

This flag removes all safety restrictions entirely, including the sandbox. Codex can access any path on the filesystem, execute arbitrary commands, and make unrestricted network calls. Use this only in fully isolated, ephemeral CI runners where you have complete trust in the task prompt.

!

The word "dangerously" is intentional. Running this flag on a local development machine can cause unintended system-level changes. For production CI pipelines, prefer --full-auto unless you have a specific, well-understood need to bypass sandbox restrictions.

Exit Codes

codex exec follows Unix exit code conventions and is designed to integrate cleanly with shell scripts and CI systems.

Exit code Meaning Common causes
0 Success Task completed as expected; Codex determined the goal was achieved
1 Task execution failed Model could not understand the instruction; file operation blocked by sandbox; task description too vague to produce a complete result
2 Configuration or parameter error OPENAI_API_KEY not set; invalid --model value; malformed config file

Using exit codes in shell scripts:

Handling exit codes in shell scripts
#!/bin/bash
set -e  # Exit immediately if any command fails

# Run a task — will abort the script if it exits non-zero (due to set -e)
codex exec --full-auto --quiet "Update CHANGELOG.md"

# Explicit exit code check
if codex exec --full-auto "Run tests and fix any failing cases"; then
  echo "Task succeeded, ready to commit"
  git add -A && git commit -m "chore: AI auto-fixed tests"
else
  EXIT_CODE=$?
  echo "Task failed with exit code: $EXIT_CODE"
  exit $EXIT_CODE
fi
i

GitHub Actions and GitLab CI both automatically mark a step as failed when the exit code is non-zero and stop subsequent steps — no extra handling needed for the common case. You only need explicit exit code logic when implementing "fail-and-continue-but-log" patterns.

GitHub Actions Integration

Below is a complete, production-grade GitHub Actions workflow that runs Codex on every pull request to perform an AI code review and update the changelog.

.github/workflows/codex-ai-review.yml
name: AI Code Review & Changelog

on:
  pull_request:
    branches: [main, develop]
    paths:
      - 'src/**'
      - 'lib/**'
      - '!**/*.md'

permissions:
  contents: write
  pull-requests: write

jobs:
  ai-review:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Full history required for git diff
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Install project dependencies
        working-directory: .
        run: npm ci

      - name: Install Codex CLI
        run: npm install -g @openai/codex

      - name: Run AI code review
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        working-directory: .
        run: |
          codex exec \
            --model gpt-4.1-mini \
            --full-auto \
            --quiet \
            "Review the changes in this PR (git diff origin/main...HEAD) and write a code review report to .ai-review.md, covering: potential bugs, performance issues, and security vulnerabilities."

      - name: Update CHANGELOG
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        working-directory: .
        run: |
          codex exec \
            --model gpt-4.1-mini \
            --full-auto \
            --quiet \
            "Based on git diff origin/main...HEAD, prepend an Unreleased entry to CHANGELOG.md following Keep a Changelog format. Only modify CHANGELOG.md."

      - name: Commit AI changes
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add .ai-review.md CHANGELOG.md
          git diff --staged --quiet || git commit -m "chore(ai): update review and changelog [skip ci]"
          git push

Key details: permissions: contents: write is required to allow the workflow to push commits back to the repo. fetch-depth: 0 ensures git diff origin/main...HEAD works correctly. The git diff --staged --quiet || guard prevents an empty commit from failing the step when no files were changed.

GitLab CI Integration

GitLab CI integration follows the same pattern as GitHub Actions. The key differences are the config file format and how secrets are injected.

i

To add OPENAI_API_KEY in GitLab: go to your repository → Settings → CI/CD → Variables → Add variable. Check both Masked (prevents the value from appearing in job logs) and Protected (restricts the variable to protected branches only).

.gitlab-ci.yml
stages:
  - ai-review
  - deploy

variables:
  NODE_VERSION: "22"

ai-code-review:
  stage: ai-review
  image: node:22-slim
  only:
    - merge_requests
  variables:
    OPENAI_API_KEY: $OPENAI_API_KEY  # Injected from GitLab CI/CD Variables
  before_script:
    - npm install -g @openai/codex
    - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
  script:
    - |
      codex exec \
        --model gpt-4.1-mini \
        --full-auto \
        --quiet \
        "Review the changes in this MR (git diff origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}),
        write findings to .ai-review.md: code quality issues, potential bugs, security problems.
        Only modify .ai-review.md, do not touch any other files."
    - |
      codex exec \
        --model gpt-4.1-mini \
        --full-auto \
        --quiet \
        "Based on git diff origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME},
        prepend an Unreleased entry to CHANGELOG.md following Keep a Changelog format.
        Only modify CHANGELOG.md."
  after_script:
    - git config user.email "[email protected]"
    - git config user.name "GitLab CI"
    - git add .ai-review.md CHANGELOG.md
    - git diff --staged --quiet || git commit -m "chore(ai): update review and changelog [skip ci]"
    - git push "https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
        "HEAD:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}"
  timeout: 10 minutes
  interruptible: true

Practical Script Examples

The following are ready-to-use bash one-liners and short scripts covering the most common automation scenarios.

Auto-generate CHANGELOG entries

Generate CHANGELOG
# Read the last 10 commits and prepend a CHANGELOG entry
$ codex exec --model gpt-4.1-mini --full-auto --quiet \
    "Read git log --oneline -10, prepend a ## [Unreleased] block to CHANGELOG.md with one bullet per commit. Format: - type: description"

Auto-fix lint errors

Lint auto-fix
# Run lint and automatically fix all fixable errors
$ codex exec --full-auto --quiet \
    "Run npm run lint 2>&1, analyze the output, fix all auto-fixable ESLint errors in src/, then run lint again to confirm zero errors"

Batch JSDoc generation

JSDoc generation
# Generate JSDoc for all exported functions in a file
$ codex exec --approval-mode auto-edit \
    "Add complete JSDoc comments to all exported functions in src/utils/helpers.ts, including @param, @returns, and @example. Do not change any function implementations."

Generate unit tests (with timeout guard)

Test generation with timeout protection
# Use the timeout command to prevent the job from hanging indefinitely (5-minute limit)
$ timeout 300 codex exec --full-auto \
    "Write Vitest unit tests for every exported function in src/auth/login.ts. Save to src/auth/__tests__/login.test.ts, covering happy path and edge cases."
RESULT=$?
if [ $RESULT -eq 124 ]; then
  echo "Timed out! Consider breaking this into smaller sub-tasks."
fi

Cost Control

Frequent AI API calls in CI/CD pipelines can accumulate significant costs. The following strategies have been validated in production to keep spending under control.

Choose the right model for the task

Cost differences between models are enormous — matching model capability to task complexity is the single highest-impact cost lever:

Model Relative cost Best for
gpt-4.1 High (baseline) Complex security audits, deep refactoring, tasks requiring strong reasoning
gpt-4.1-mini Low (~1/20th) CHANGELOG generation, code formatting, simple annotations, everyday PR checks
gpt-4.1-nano Very low (~1/100th) Simple text classification, format conversion, keyword extraction
o4-mini Medium (billed by reasoning tokens) Multi-step reasoning, complex logic analysis, algorithm optimization
Cost-optimized command patterns
# Low-cost task: mini model + quiet mode (reduces output tokens too)
$ codex exec --model gpt-4.1-mini --quiet --full-auto \
    "Update CHANGELOG.md"

# Timeout guard: prevents runaway token consumption from stuck tasks
$ timeout 180 codex exec --model gpt-4.1-mini --quiet \
    "Summarize recent changes in src/ and write a brief report"

# Path filtering in GitHub Actions (set on.pull_request.paths in YAML)
# ensures the workflow only runs when source files actually change

Optimize trigger conditions

  • Path filters: Use on.pull_request.paths in GitHub Actions to run the AI job only when source code directories change, not on documentation updates.
  • Branch filters: Only trigger on PRs targeting main/develop, not on every push to every feature branch.
  • Manual dispatch: For expensive full-codebase tasks like complete test generation, use workflow_dispatch to trigger manually instead of automatically on every PR.
  • Concrete task descriptions: More specific prompts generally consume fewer tokens, because the model doesn't need to infer what you want.

Common Errors & Troubleshooting

Exit code 1 — Task description too vague

One of the most common failure causes. When a task description is ambiguous, the model may believe it has completed some form of the task (e.g., printing an analysis to stdout) without achieving the actual intended outcome, or it cannot determine what "done" looks like and exits with failure.

Solution: Be explicit in your task description about:

  • Exact file paths to read or modify
  • Expected output format
  • A clear success criterion (e.g., "run npm test — all tests must pass")
Vague vs. specific task descriptions
# Bad: vague, prone to exit code 1
codex exec "check my code"

# Good: specific, includes file path and expected output
codex exec --full-auto \
  "Review all .ts files in src/api/, write findings (one per line) to code-issues.txt"
Exit code 2 — OPENAI_API_KEY environment variable not set

Codex checks for the OPENAI_API_KEY environment variable on startup. If it is missing, Codex exits immediately with code 2.

Troubleshooting steps:

  1. Verify locally: echo $OPENAI_API_KEY should print a non-empty value
  2. In GitHub Actions, confirm the env block is nested directly under the relevant step
  3. Check that the secret name matches exactly — it is case-sensitive and must be OPENAI_API_KEY
  4. Verify in the OpenAI dashboard that the key has not expired and has a positive credit balance
Correct env injection in GitHub Actions
- name: Run codex exec
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}  # ← must be at step level
  run: codex exec --full-auto "your task here"
Sandbox blocking writes — file operations rejected

In suggest mode or with restricted sandbox settings, Codex will not write files. This does not crash the script, but the task has not actually made any file changes even if it exits 0.

Solution: For tasks that need to modify files, use --full-auto. If the task needs to write outside the working directory, consider --dangerously-auto-approve-everything only after carefully assessing the security implications.

Task timeout — CI job takes too long

Codex can take a while on complex tasks, especially when analyzing large codebases. To prevent runaway jobs:

  • Add timeout-minutes: 10 to the GitHub Actions step
  • Wrap the command: timeout 300 codex exec ... (300 seconds)
  • Break large tasks into multiple smaller, more specific tasks and run them sequentially
  • Use --model gpt-4.1-mini for faster response times with lower latency
git diff returns empty — cannot analyze PR changes

GitHub Actions uses a shallow clone by default (fetch-depth: 1), which means git diff origin/main and similar commands will fail or return empty output.

Fix: set fetch-depth in checkout step
- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # 0 = full history; supports any git diff command

FAQ

What is the difference between codex exec and running codex directly?

codex (no subcommand) opens a full-screen interactive TUI where you type instructions and confirm each action in real time. codex exec accepts a task string as an argument, bypasses all UI, executes the task, and exits automatically with a meaningful exit code. Interactive mode is built for humans; exec is built for machines.

Can I use codex exec on Windows?

Yes, but running inside Windows Subsystem for Linux (WSL2) is strongly recommended for best compatibility. Native Windows PowerShell or CMD can have issues with path handling and the sandbox mechanism. For CI pipelines, simply use runs-on: ubuntu-latest.

Does --quiet affect exit codes?

No. --quiet only suppresses intermediate progress output to stdout; it has no effect whatsoever on the exit code logic. Success still returns 0, task failure still returns 1, and config errors still return 2. Using --quiet in CI is recommended to reduce log noise while keeping full exit-code semantics intact.

Can I use a custom AGENTS.md with codex exec?

Yes. codex exec automatically reads an AGENTS.md file in the project root and includes it as part of the system prompt, just as interactive mode does. You can use AGENTS.md to define code style rules, prohibited file paths, required output formats, and other behavioral constraints that apply across all codex exec invocations in that repository.

How can I verify which files codex exec actually changed?

Run git diff or git status immediately after codex exec completes to see all modified files. In CI pipelines, add a step after the codex exec step with run: git diff --stat to print a change summary to the job log for auditing and debugging purposes.