Sandbox causes significant managed `apply_patch` latency under `workspace-write` on Linux/WSL2

Open 💬 1 comment Opened Aug 13, 2026 by dylbarne

What version of Codex CLI is running?

0.147.0

What subscription do you have?

codex

Which model were you using?

gpt-5.6-sol

What platform is your computer?

Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 x86_64

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

WezTerm

Codex doctor report

What issue are you seeing?

Managed direct apply_patch operations incur several seconds of latency per filesystem mutation under workspace-write.

Representative timings:

| Operation | workspace-write | danger-full-access |
|---|---:|---:|
| Create | 3.6–5.3s | 8ms |
| Update | 6.0s | 10ms |
| Delete | 5.5–7.2s | 9ms |

The bundled local apply_patch executable remains effectively instantaneous when invoked through sandboxed exec_command. Git and ordinary filesystem operations are also fast.

There are no error messages—the issue is deterministic pre/post-mutation latency in the managed direct-tool/sandbox path. This appears related to #35376

What steps can reproduce the bug?

Environment:

  • Codex CLI 0.147.0
  • WSL2/Linux x86_64
  • Native ext4 filesystem
  • approval_policy = "on-request"
  1. Configure and restart Codex:
sandbox_mode = "workspace-write"
approval_policy = "on-request"
  1. Give Codex this prompt:
Using the managed direct apply_patch tool, measure each operation separately:

1. Create /tmp/codex-managed-patch-probe.txt containing "created".
2. Read and verify it.
3. Update "created" to "updated" using apply_patch.
4. Read and verify it.
5. Delete it using apply_patch.
6. Verify it no longer exists.

Report wall-clock milliseconds for every operation.
Do not substitute shell redirection or another file-writing mechanism.
  1. Managed create/update/delete operations take approximately 3–7 seconds each.
  1. As a control, invoke the bundled patch executable through exec_command:
/usr/bin/time apply_patch <<'PATCH'
*** Begin Patch
*** Add File: /tmp/codex-local-patch-probe.txt
+probe
*** End Patch
PATCH

The actual local patch operation completes in effectively 0.00 seconds.

  1. Change only the sandbox mode and restart Codex:
sandbox_mode = "danger-full-access"
  1. Repeat the managed direct-tool prompt. Create/update/delete complete in approximately 8–10ms each.
  1. Restore workspace-write and repeat. The multi-second latency returns.

Removing /tmp from explicit writable_roots does not resolve the issue because it remains writable through the default workspace policy.

This isolates the slowdown to the interaction between workspace-write and the managed direct apply_patch filesystem bridge, rather than patch parsing, WSL, ext4, or ordinary sandboxed command execution.

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

1 Comment

jdcodes1 · 9 days ago

The latency is per-process-spawn, and the managed path spawns a lot of processes. Every filesystem primitive in the managed apply_patch path — canonicalize, read, write, metadata, remove — goes through run_sandboxed, and each call builds a fresh sandbox command and spawns a new re-exec of the entire codex binary as the fs helper (FsSandboxRunner::runsandbox_commandrun_command, helper = codex_self_exe: https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/exec-server/src/fs_sandbox.rs#L65-L73, #L120-L139). One patch is several such spawns.

Under workspace-write on WSL2 each spawn pays: loading the large codex image again, bwrap/landlock setup, and WSL2's slow process-creation + (if the binary lives under /mnt/c) DrvFS/9P reads. A few hundred ms–1s per spawn × several ops per file lands exactly in your 3.6–7.2 s range. danger-full-access never enters this path (no enforced sandbox → in-process file I/O → 8–10 ms), and sandboxed exec_command is fast because it spawns your small target once, not codex-per-fs-call. Same helper-re-exec design is implicated in #38286.

Fix outline: (1) keep one sandboxed helper alive per session/environment and stream FsHelperRequests over its stdio — the JSON request protocol already exists; (2) or batch a whole verified patch into a single helper invocation; (3) optionally a small dedicated helper binary instead of re-execing codex.