codex - 💡(How to fix) Fix Codex sandbox loses captured stdout/stderr from nested Node.js child processes writing via `process.stdout` / `process.stderr` [1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#18473Fetched 2026-04-19 15:12:41
View on GitHub
Comments
1
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×4commented ×1renamed ×1

Error Message

  • console.error(...) node -e 'const {spawnSync}=require("node:child_process"); const r=spawnSync(process.execPath,["-e","console.log("child-out"); console.error("child-err")"],{encoding:"utf8"}); console.log(JSON.stringify({status:r.status,stdout:r.stdout,stderr:r.stderr}));'

Root Cause

More specifically, if a Node process launches another Node process using captured pipe stdio, the child may exit successfully but the parent still receives empty captured output. Reproduced this with spawn, spawnSync, exec, execFile, and fork({ silent: true }). Default fork() is unaffected because its stdout/stderr are forwarded to the parent.

Code Example

node -e 'const {spawnSync}=require("node:child_process"); const r=spawnSync(process.execPath,["-e","console.log(\"child-out\"); console.error(\"child-err\")"],{encoding:"utf8"}); console.log(JSON.stringify({status:r.status,stdout:r.stdout,stderr:r.stderr}));'

---

{"status":0,"stdout":"","stderr":""}

---

{"status":0,"stdout":"child-out\n","stderr":"child-err\n"}
RAW_BUFFERClick to expand / collapse

What version of Codex CLI is running?

codex-cli 0.121.0

What subscription do you have?

ChatGPT Plus

Which model were you using?

gpt-5.4

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 x86_64

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

No response

What issue are you seeing?

In the Codex sandbox, captured stdout / stderr from a nested Node child process can be lost when the child writes through Node’s default process.stdout / process.stderr streams.

More specifically, if a Node process launches another Node process using captured pipe stdio, the child may exit successfully but the parent still receives empty captured output. Reproduced this with spawn, spawnSync, exec, execFile, and fork({ silent: true }). Default fork() is unaffected because its stdout/stderr are forwarded to the parent.

In the failing case, these child-side writes produced no captured bytes in the parent:

  • console.log(...)
  • console.error(...)
  • process.stdout.write(...)
  • process.stderr.write(...)

This appears to be specific to captured pipe/socket-backed stdio in the sandbox. The following control cases worked correctly:

  • top-level node output is captured normally
  • stdio: "inherit" shows the child output normally
  • file-backed stdio works normally
  • fd-level writes such as fs.writeSync(1/2, ...), fs.write(1/2, ...), and fs.createWriteStream({ fd: 1/2 }) are captured correctly
  • Node -> Python, Node -> bash, and Python -> Node capture work correctly

Inside the failing nested child, fd 1 and fd 2 are socket-backed, but Node initializes process.stdout / process.stderr differently than it does outside the sandbox. In the sandbox, they appear as plain Writable streams with no backing handle; outside the sandbox, they appear as Socket objects with a backing Pipe handle. This suggests a sandbox-specific issue in Node/libuv stdio initialization or handling for captured stdio.

This causes false negatives in automated verification: a tool may emit diagnostics or progress normally when run interactively, but Codex receives empty captured stdout / stderr.

What steps can reproduce the bug?

Run the following inside the Codex sandbox:

node -e 'const {spawnSync}=require("node:child_process"); const r=spawnSync(process.execPath,["-e","console.log(\"child-out\"); console.error(\"child-err\")"],{encoding:"utf8"}); console.log(JSON.stringify({status:r.status,stdout:r.stdout,stderr:r.stderr}));'

Observed result in the sandbox:

{"status":0,"stdout":"","stderr":""}

The same missing-output behavior reproduces with the other captured-stdio child_process APIs listed above.

Uploaded thread: 019d9fe5-7dd6-7890-a44a-6d4c366a376a

What is the expected behavior?

The parent process should receive the child Node process’s captured output normally:

{"status":0,"stdout":"child-out\n","stderr":"child-err\n"}

The same expectation applies to all the captured-stdio child_process APIs listed above.

Additional information

  • Reproduced in the Codex sandbox on WSL2 Ubuntu 24.04 with Node v24.15.0.
  • The same repro behaves correctly outside the sandbox.
  • This does not appear to be just a flush-on-exit race:
    • adding short delays before exit did not help
    • write callbacks still fired
    • .write() returned success
    • calling .end() on process.stdout / process.stderr did not restore captured output
  • Possibly separate observation: the sync variants (spawnSync, execSync, execFileSync) can also surface an EPERM artifact even when the child appears to have run and exited with status === 0. This seems separate from the main missing-output bug, which also reproduces with async APIs (spawn, exec, execFile, fork({ silent: true })) where no such artifact is surfaced.

extent analysis

TL;DR

The issue can be worked around by using stdio: "inherit" or file-backed stdio instead of captured pipe/socket-backed stdio in the Codex sandbox.

Guidance

  • The issue appears to be specific to captured pipe/socket-backed stdio in the sandbox, so using stdio: "inherit" or file-backed stdio may resolve the issue.
  • Verify that the child process's output is being captured correctly by checking the stdout and stderr properties of the result object returned by the child process API.
  • To mitigate the issue, consider using fs.writeSync(1/2, ...), fs.write(1/2, ...), or fs.createWriteStream({ fd: 1/2 }) to write to the file descriptors directly instead of using console.log() or process.stdout.write().
  • The issue may be related to Node/libuv stdio initialization or handling for captured stdio in the sandbox, so further investigation into this area may be necessary.

Example

const { spawnSync } = require("node:child_process");
const r = spawnSync(process.execPath, ["-e", "console.log(\"child-out\"); console.error(\"child-err\")"], { stdio: "inherit" });

This example uses stdio: "inherit" to forward the child process's output to the parent process's stdout and stderr.

Notes

  • The issue only reproduces in the Codex sandbox and not outside of it, suggesting that it may be specific to the sandbox environment.
  • The issue affects multiple child process APIs, including spawn, spawnSync, exec, execFile, and fork({ silent: true }).

Recommendation

Apply workaround: use stdio: "inherit" or file-backed stdio instead of captured pipe/socket-backed stdio in the Codex sandbox. This should resolve the issue and allow the child process's output to be captured correctly.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix Codex sandbox loses captured stdout/stderr from nested Node.js child processes writing via `process.stdout` / `process.stderr` [1 comments, 1 participants]