Linux sandbox: node child_process.spawnSync/execFileSync returns EPERM after the child exits successfully (status 0, stdout captured)

Open 💬 0 comments Opened Aug 27, 2026 by TTT3P

Summary

Inside the Linux sandbox (codex sandbox -- …, and equally inside an agent turn under sandbox_mode = "workspace-write"), Node's child_process.spawnSync / execFileSync fails with EPERM after the child has already run successfully. The child executes and its stdout is captured (status: 0), but Node reports error: spawnSync <cmd> EPERM (errno -1), so execFileSync throws. Any build/tooling script that shells out from Node (e.g. git rev-parse HEAD) breaks. The same command outside the sandbox works.

Environment

  • codex-cli 0.150.1 (native Linux binary), WSL2 Ubuntu 26.04 LTS, kernel per WSL2, system bubblewrap 0.11.1 at /usr/bin/bwrap
  • Node 24.20.0 / 22.23.2 / 20.20.2 (all reproduce identically)
  • macOS (seatbelt) does not reproduce

Repro

$ codex sandbox -- git rev-parse --short HEAD
16ce57d                                   # plain git is fine

$ codex sandbox -- node -e 'const r=require("child_process").spawnSync("git",["rev-parse","--short","HEAD"]);console.log("status",r.status,"err",r.error?r.error.code:"none","out",String(r.stdout).trim())'
status 0 err EPERM out 16ce57d            # child ran, output captured, but error=EPERM

execFileSync('git', ['rev-parse','HEAD']) therefore throws spawnSync git EPERM even though git returned 0 and printed the sha.

What we ruled out

  • Node version: 24 / 22 / 20 identical.
  • features.use_legacy_landlock = true (via --enable, -c, and in CODEX_HOME/config.toml): still EPERM.
  • use_linux_sandbox_bwrap true/false in config.toml: still EPERM.
  • Running the same node one-liner directly under bwrap (including --unshare-pid --as-pid-1) does not reproduce, so it does not look like the pid namespace alone; it looks like the syscall filter the sandbox runner applies around exec (the child's post-exit reap path in libuv — waitid/pidfd_*/kill(pid,0) class — gets EPERM).

Expected

spawnSync/execFileSync of an allowed command inside the sandbox should return without error when the child exits 0, as it does on macOS.

Impact

Any Node-based build step that spawns processes (git, npm run scripts calling git, etc.) fails inside the Linux sandbox, forcing users to either move work off Linux or use danger-full-access, which we'd rather not.

View original on GitHub ↗