openai / openai/codex

codex exec fails in an outer read-only sandbox because installation_id requires write access

Open
#42,398 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app-server bug CLI exec sandbox
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of Codex CLI is running?

codex-cli 0.151.0

The rust-v0.152.0 source has an identical installation_id.rs and still calls its resolver during in-process app-server startup. 0.152.0 was inspected, not installed or run.

What subscription do you have?

Pro

Which model were you using?

No model turn was reached; the CLI failed during startup.

What platform is your computer?

macOS 27.0, Apple Silicon (arm64).

What terminal emulator and version are you using (if applicable)?

The CLI was launched as a subprocess from a Codex Desktop task.

Codex doctor report

Not collected. Startup logs and a focused filesystem probe are described below.

What issue are you seeing?

A nested codex exec fails before emitting any JSON events when the enclosing task sandbox allows reading ~/.codex/installation_id but denies writing it:

Error: failed to initialize in-process app-server client: Operation not permitted (os error 1)

Two launches exited with code 1 and empty stdout, requesting inner read-only and workspace-write sandboxes respectively. Both inherited the same outer restrictions. Redirecting logs and SQLite to writable fixture directories, disabling analytics, and using --ephemeral did not avoid the failure.

This blocks starting a read-only worker with existing Codex state. The diagnostic does not identify which file requires write access.

What steps can reproduce the bug?
  1. On macOS, launch a child CLI from an enclosing Codex task that denies writes to ~/.codex/installation_id while allowing reads. The observed file already existed, was user-owned, and had mode 0644.

  2. Provide a synthetic Git repository and writable directories for logs, SQLite, temporary files, and caches. Retain the existing Codex home.

  3. Launch codex exec with these relevant arguments:

    codex -a never exec --sandbox read-only --ephemeral --json \
      -C /path/to/synthetic-repo \
      -c 'log_dir="/path/to/writable-logs"' \
      -c 'sqlite_home="/path/to/writable-sqlite"' \
      -c 'analytics.enabled=false' \
      -c 'history.persistence="none"' \
      "Read sample.py."
    

    This is a shortened representation of the recorded invocation, not an independently tested minimal reproducer. The actual launches also configured a fixture MCP server, disabled unrelated plugins/hooks, and used longer validation prompts. The fixture MCP connection was never reached.

  4. Observe the startup error above. Setting the inner CLI's --sandbox read-only alone does not create the required outer restriction; that flag controls the worker's tool execution.

A separate probe in the same enclosing sandbox confirmed:

Operation on the existing installation-ID file Result
os.open(path, os.O_RDONLY) Succeeds
os.open(path, os.O_RDWR) Fails with EPERM / errno 1
File contents before and after the probe Unchanged
What is the expected behavior?

Reusing a valid existing installation ID should not require opening the file for writing. If startup requires a write, its error should name the path and operation that failed.

Additional information

In 0.151.0's resolver, the file is opened with:

options.read(true).write(true).create(true);

This happens before reading and validating the existing UUID. 0.152.0 retains that implementation, and app-server startup propagates its error.

The denied writable open and this call path identify a startup blocker. A patched binary has not been tested, so this report does not establish that removing this blocker is sufficient for the entire nested session to run.

Consider reading and reusing a valid ID without a writable open, retaining the locked write path for creation or repair. Regression coverage should include an existing valid ID with writes denied, missing/invalid IDs in a writable home, and concurrent initialization.

Related upstream work:

  • #16912 introduced the persistent installation ID and writable-open behavior.
  • #21182 moved resolution to host startup and let internal child sessions reuse the parent's ID. A separately launched CLI still resolves the file itself.
  • #26277 discusses Android file-lock failures in the same startup path; the observed failure here occurs when opening the file for writing on macOS.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with codex-rs/core/src/installation_id.rs, focusing on the resolver's file-open behavior, then trace its propagation through codex-rs/app-server/src/in_process.rs. Add regression coverage for an existing valid ID with writes denied, missing or invalid IDs in a writable home, and concurrent initialization; done means the read-only case reuses the ID without requiring write access.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, operating-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.