Skip to content

Unified exec caches stale ~/.codex/tmp/arg0 session path and fails with ENOENT until app restart #16970

Open

Description

@fy-nn

What version of Codex is running?

codex 26.325.31654 app
codex-cli 0.118.0-alpha.2

What subscription do you have?

plus

Which model were you using?

gpt-5.3 Codex (Codex desktop default coding model)

What platform is your computer?

Windows 11 + WSL2
Linux kernel: Linux 6.6.87.2-microsoft-standard-WSL2 x86_64

What issue are you seeing?

In an active Codex Desktop thread, non-escalated tool execution suddenly fails with ENOENT from unified exec.
The failing spawn target points to a temp runtime path under ~/.codex/tmp/arg0/<session>/codex-linux-sandbox that no longer exists.

After restarting the app, a new codex-arg0* runtime folder is created and tool execution works again.

What steps can reproduce the bug?

Intermittent, but this sequence reproduces it for me:

  1. Start Codex Desktop in a WSL-backed workspace.
  2. Use normal tool calls for a while (exec_command, apply_patch, etc.).
  3. At some point, all non-escalated exec_command calls fail immediately.
  4. Error references a specific stale temp path:
    .../tmp/arg0/codex-arg06KNIXk/codex-linux-sandbox ... ENOENT
  5. Confirm that folder no longer exists.
  6. Restart Codex Desktop.
  7. A new folder appears (example: codex-arg0nHm5Gj) containing codex-linux-sandbox, and commands succeed again.

What is the expected behavior?

If an ephemeral codex-arg0* runtime folder is missing/rotated, unified exec should automatically rebind/recreate sandbox shims instead of hard-failing until full app restart.

What do you see instead?

Exact error captured in session log:

Failed to create unified exec process: Unable to spawn /mnt/c/Users/<redacted>/.codex/tmp/arg0/codex-arg06KNIXk/codex-linux-sandbox because it doesn't exist on the filesystem (ENOENT: No such file or directory)

Additional information

I checked related issues before filing:

This report is specifically about a stale ~/.codex/tmp/arg0/codex-arg* runtime path being referenced by unified exec during an active desktop session.

Local evidence:

  • Failing log:
    /mnt/c/Users/<redacted>/.codex/sessions/2026/04/07/rollout-2026-04-07T10-37-51-019d655f-d19c-75e0-9fea-dc5638991fd9.jsonl
    (entry timestamp 2026-04-07T01:06:04.320Z)
  • Missing stale folder after failure:
    ~/.codex/tmp/arg0/codex-arg06KNIXk (not found)
  • Healthy after restart:
    ~/.codex/tmp/arg0/codex-arg0nHm5Gj/codex-linux-sandbox -> ~/.codex/bin/wsl/codex
### Sanitized diagnostic excerpt

[2026-04-07T01:06:04.320Z]
exec_command failed for `/bin/bash -lc ''`: CreateProcess {
  message: "Rejected(\"Failed to create unified exec process: Unable to spawn /mnt/c/Users/<redacted>/.codex/tmp/arg0/codex-arg06KNIXk/codex-linux-sandbox because it doesn't exist on the filesystem (ENOENT: No such file or directory)\")"
}

[2026-04-07T01:09:36.879Z]
Command: /bin/bash -lc 'ls -la /mnt/c/Users/<redacted>/.codex/tmp/arg0/codex-arg06KNIXk 2>/dev/null || true'
Output: <empty>  (folder missing)

[2026-04-07T01:17:00.619Z]
Command: /bin/bash -lc 'ls -la /mnt/c/Users/<redacted>/.codex/tmp/arg0'
Output:
  ... codex-arg0nHm5Gj

[2026-04-07T01:17:21.860Z]
Command: /bin/bash -lc 'ls -la /mnt/c/Users/<redacted>/.codex/tmp/arg0/codex-arg0nHm5Gj'
Output:
  ... codex-linux-sandbox -> /mnt/c/Users/<redacted>/.codex/bin/wsl/codex

[2026-04-07T01:18:04Z]
`pwd` and `node -v` succeed again in the same workspace after restart.

Environment:
- codex-cli 0.118.0-alpha.2
- Windows 11 + WSL2

Root-cause hypothesis:

  • Unified exec/harness caches a per-session sandbox shim path under tmp/arg0.
  • If that ephemeral folder is removed/rotated, the cached path is reused and all non-escalated spawns fail with ENOENT.

High-level fix suggestion:

  • On ENOENT for the sandbox shim path, force a one-time runtime rehydrate/rebind:
    • recreate the per-session symlink in a fresh codex-arg0* folder, or
    • fall back to ~/.codex/bin/wsl/codex and rebuild session shims.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    windows-osIssues related to Codex on Windows systems
    sandboxIssues related to permissions or sandboxing
    tool-callsIssues related to tool calling
    on Apr 7, 2026
  2. removed
    sandboxIssues related to permissions or sandboxing
    appIssues related to the Codex desktop app
    on Apr 7, 2026
  3. fy-nn commented on Apr 7, 2026

    @fy-nn
    Author

    Uploaded diagnostics via /feedback.

    Thread ID: 019d655f-d19c-75e0-9fea-dc5638991fd9
    Time: 2026/04/07 - 2:05pm
    Repro summary: unified exec failed with ENOENT for missing ~/.codex/tmp/arg0/.../codex-linux-sandbox; restart recreated a new codex-arg0* folder and resolved it.

    Related issue for overlap context: #16791

  4. brucek2 commented on Apr 7, 2026

    @brucek2

    Same here. I have to quit the Codex app and delete the .codex/tmp directory every time I switch project folders. Otherwise apply_patch and other basic tooling is broken. (also, fwiw, Codex did not feel this issue was a clear match to any of the potential dupes noted above.)

    Here's Codex's own report:
    Confirmed on Codex Desktop + Windows 11 + WSL2.

    Repro pattern:

    work in one WSL-backed repo
    switch to another repo/workspace in the app without quitting Codex
    after one or more switches, apply_patch starts failing with No such file or directory
    some ordinary non-escalated exec_command calls also fail with Failed to create unified exec process: No such file or directory (os error 2)
    Key evidence:

    the active session environment had PATH starting with %USERPROFILE%\.codex\tmp\arg0\codex-arg0...
    that exact codex-arg0... directory no longer existed
    after restart, PATH pointed at a fresh existing codex-arg0... dir and both apply_patch and sandboxed exec/write worked again
    This does not look like patch grammar parsing. It looks like stale temp/session wrapper state after workspace switching.

    Recovery that worked:

    quit Codex Desktop
    delete .codex/tmp in both Windows-side and WSL-side homes
    restart Codex
    In my case that restored:

    sandboxed shell write
    apply_patch
    normal non-escalated exec
    This seems very close to the root cause described here: the live session retains a dead arg0 temp-wrapper path until restart.

  5. MisterRound commented on May 15, 2026

    @MisterRound

    Additional confirmation from Windows Desktop + WSL mode on Codex Desktop OpenAI.Codex_26.506.3741.0_x64__2p2nqsd0c76g0, WSL 2.7.3.0, Codex WSL binary reporting codex-cli 0.130.0-alpha.5.

    I hit the stale helper-dir failure exactly as described here:

    • Live PATH contained /mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0i3QOLP.
    • That directory no longer existed.
    • Earlier in the session it had contained Linux helpers: apply_patch, applypatch, codex-execve-wrapper, codex-linux-sandbox.
    • Later helper dirs under the same tmp/arg0 root existed but contained only Windows .bat apply-patch shims.
    • Normal shell/tool calls failed before command execution with No such file or directory.
    • The workspace path itself existed; the failure was the cached helper path, not cwd.

    A full reboot/Desktop restart regenerated a healthy helper dir with Linux symlinks to /mnt/c/Users/<user>/.codex/bin/wsl/codex and shell calls started working again.

    This supports the fix direction in #17570: active helper dirs need to be protected from cleanup/rotation, especially when the WSL runtime uses a Windows-mounted CODEX_HOME under /mnt/c.

  6. MisterRound commented on May 30, 2026

    @MisterRound

    Additional current data point from Windows Desktop + WSL: this stale tmp/arg0 helper class is still reproducible in OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0, and the recovery behavior appears worse than before.

    Important difference from the original report:

    • unified_exec=false is already set in both Windows and WSL Codex configs.
    • The machine was fully rebooted more than once.
    • Reboot/relaunch is no longer a reliable recovery; a fresh Desktop + WSL app-server session can still reach the stale-helper state.

    Live evidence from the affected session:

    PATH contains:
    /mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV
    
    stat:
    No such file or directory
    
    /proc/<app-server-pid>/fd/3:
    /mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV/.lock
    
    fdinfo:
    POSIX ADVISORY WRITE lock owned by the live app-server pid
    

    So the WSL app-server still has an fd/lock for the old .lock inode, but the helper directory path is gone. Later/newer codex-arg0* directories can be created with valid Linux symlinks, but the active thread remains pinned to the deleted path and shell/tool calls still fail before command execution.

    I opened a focused regression issue here: #25317

    This strengthens the root-cause direction in #17570, but also suggests the fix may need to cover the mixed Windows/WSL shared-home case, not only Linux-side lock unreliability.

  7. wh-wang132 commented on Jul 13, 2026

    @wh-wang132

    Still reproducible on a newer Windows Desktop build.

    Environment

    • Codex Desktop package: OpenAI.Codex_26.707.8479.0_x64__2p2nqsd0c76g0
    • WSL app-server: codex-cli 0.144.2
    • Windows 11 build 26200
    • WSL 2.7.10, Ubuntu
    • Repository stored on the WSL filesystem under /root/test_cpp

    Observed behavior

    • Shell execution initially works.
    • After the Desktop app has been running for some time, every normal exec_command fails before the requested command starts.
    • The same failure occurs with zsh, bash, and sh:
    Failed to create unified exec process: No such file or directory (os error 2)
    
    • Direct wsl.exe shell commands remain healthy.
    • Restarting Codex Desktop restores shell execution.
    • After restart, Desktop creates a fresh helper directory such as:
    C:\Users\<user>\.codex\tmp\arg0\codex-arg04kNAGN
    

    containing Linux symlinks for apply_patch, applypatch, codex-execve-wrapper, and codex-linux-sandbox.

    Desktop startup logs show unified_exec enabled.

    I did not capture the failed app-server's PATH or deleted helper directory before restarting, so I cannot yet prove that the live session referenced a deleted arg0 directory. However, the failure signature, healthy direct WSL shell, delayed onset, and restart recovery closely match this issue.

    Separately, the Desktop git watcher produces repeated EISDIR errors on WSL UNC paths. That appears to be the distinct watcher problem tracked in #17991, rather than sufficient evidence that the watcher caused this shell failure.

  8. Eric-SHENNONGSHI commented on Sep 22, 2026

    @Eric-SHENNONGSHI

    I reproduced the same stale/deleted tmp/arg0 helper failure on native Linux through the VS Code Agent Window, without Windows or WSL.

    Environment:

    • Ubuntu 22.04, Linux 6.8.0-138-generic, x86_64
    • VS Code 1.138.0
    • VS Code Agent Host Codex CLI 0.153.0
    • OpenAI ChatGPT/Codex extension 26.908.40401-linux-x64
    • Session originator: vscode_agent_host
    • Sandbox: managed workspace-write with restricted network access

    Observed sequence on 2026-09-22:

    1. Normal tool execution worked earlier in the thread.
    2. Every later exec_command, including pwd, failed before the shell started:
      bwrap: execvp /home/<user>/.codex/tmp/arg0/codex-arg0<random>/codex-linux-sandbox: No such file or directory
      
    3. Retrying with escalated permissions produced the same error.
    4. Two successive Agent Window resumptions referenced different missing helper directories, so restarting the turn did not recover it.
    5. The saved shell environment still had the missing codex-arg0<random> directory at the front of PATH.
    6. I then opened the same session through the editor window with the Codex extension. It created fresh tmp/arg0/codex-arg0* directories containing valid symlinks for apply_patch, applypatch, codex-execve-wrapper, and codex-linux-sandbox; pwd and subsequent shell commands succeeded immediately.

    This suggests the stale-helper lifecycle bug is not limited to Desktop + WSL or a shared DrvFS CODEX_HOME. The VS Code Agent Host can also resume a thread with a cached helper path after its per-process directory has disappeared. Revalidating and rehydrating/rebinding the arg0 helper on ENOENT should cover this native-Linux path as well.

    — Codex (posted on Eric's behalf via Codex)

  9. zwingsstudio commented on Sep 30, 2026

    @zwingsstudio

    Additional confirmation and a bounded cleanup-isolation proposal for the shell failure.

    On Windows Desktop + WSL with codex-cli 0.159.2, the live session referenced a missing <CODEX_HOME>/tmp/arg0/codex-arg0<session>/codex-linux-sandbox. Recreating that exact launcher symlink to the verified installed Linux Codex binary restored ordinary sandboxed shell execution without restarting or changing the sandbox configuration. The recovery and scope are documented in this earlier comment. This did not fix the separate browser/Computer Use sandboxCwd URI rejection.

    Source finding: In v0.159.2 arg0/src/lib.rs, Windows and WSL/Linux use the same <CODEX_HOME>/tmp/arg0 janitor root when they share the Windows-mounted home. The janitor checks immediate child directories using their existing .lock; Windows stages batch aliases while Linux stages executable symlinks.

    Proposed prevention: use <CODEX_HOME>/tmp/arg0/<std::env::consts::OS>/codex-arg0*. Each updated runtime then cleans only its own namespace. The legacy v0.159.2 janitor skips the platform directory because it has no immediate .lock, so it does not descend into the updated runtime's sessions. The existing alias names, locking and guard lifecycle stay intact.

    Validation: A focused Rust harness compiled the actual source functions arg0_temp_root, janitor_cleanup, and try_lock_dir, the two added tests, and the three existing janitor tests. Before the namespace change: 3 passed, 2 failed with “windows cleanup removed Linux helper” and “legacy cleanup removed namespaced helper.” After the change: 5/5 passed, including a Rust 1.95.0 run matching the repository's pinned toolchain. Focused Clippy passed with -D warnings; modified Rust files passed formatting checks. Fixtures intentionally use unheld locks: this tests namespace isolation independently of foreign file-lock visibility, not actual Windows/WSL lock interoperability.

    Build limitation: The complete just test -p codex-arg0 attempt stopped before tests ran because openssl-sys could not find Linux OpenSSL development dependencies/pkg-config. This is therefore a tested focused proposal, not a fully built or end-to-end verified Codex fix.

    Scope: We have not established which process removed the original helper directory. This proposal prevents shared-home cross-runtime janitor interference; it does not rehydrate an already-missing cached helper, fix same-runtime lifecycle/startup races, or explain the native-Linux reports above. Older binaries can still interfere with other sessions using the old layout. It is complementary to the broader stale-path recovery work suggested in this issue.

    Following the current issue-based contribution policy, here is the reviewable proposal rather than an external PR.

    Proposed patch against rust-v0.159.2 (ff6aec9)
    diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs
    index 5bed3b9..3b64fd3 100644
    --- a/codex-rs/arg0/src/lib.rs
    +++ b/codex-rs/arg0/src/lib.rs
    @@ -360,7 +360,7 @@ fn prepare_path_entry_for_codex_aliases(
     
         std::fs::create_dir_all(&codex_home)?;
         // Use a CODEX_HOME-scoped temp root to avoid cluttering the top-level directory.
    -    let temp_root = codex_home.join("tmp").join("arg0");
    +    let temp_root = arg0_temp_root(&codex_home, std::env::consts::OS);
         std::fs::create_dir_all(&temp_root)?;
         #[cfg(unix)]
         {
    @@ -452,6 +452,12 @@ fn prepare_path_entry_for_codex_aliases(
         ))
     }
     
    +fn arg0_temp_root(codex_home: &Path, runtime_os: &str) -> PathBuf {
    +    // Windows and WSL may share CODEX_HOME; scope cleanup to one runtime's helpers.
    +    // Leave the runtime directory lock-free so legacy, non-recursive janitors skip it.
    +    codex_home.join("tmp").join("arg0").join(runtime_os)
    +}
    +
     #[cfg(windows)]
     fn windows_batch_executable_path(executable: &Path, alias_directory: &Path) -> String {
         pathdiff::diff_paths(executable, alias_directory)
    @@ -535,6 +541,10 @@ fn try_lock_dir(dir: &Path) -> std::io::Result<Option<File>> {
         }
     }
     
    +#[cfg(test)]
    +#[path = "temp_root_tests.rs"]
    +mod temp_root_tests;
    +
     #[cfg(test)]
     mod tests {
         use super::Arg0DispatchPaths;
    diff --git a/codex-rs/arg0/src/temp_root_tests.rs b/codex-rs/arg0/src/temp_root_tests.rs
    new file mode 100644
    index 0000000..429bff2
    --- /dev/null
    +++ b/codex-rs/arg0/src/temp_root_tests.rs
    @@ -0,0 +1,54 @@
    +use super::LOCK_FILENAME;
    +use super::arg0_temp_root;
    +use super::janitor_cleanup;
    +use std::fs;
    +use std::fs::File;
    +use std::path::Path;
    +
    +fn create_helper_fixture(root: &Path) -> std::io::Result<std::path::PathBuf> {
    +    let session = root.join("codex-arg0fixture");
    +    fs::create_dir_all(&session)?;
    +    File::create(session.join(LOCK_FILENAME))?;
    +    let helper = session.join("helper");
    +    fs::write(&helper, b"helper sentinel")?;
    +    Ok(helper)
    +}
    +
    +#[test]
    +fn cleanup_preserves_helpers_from_other_runtimes() -> std::io::Result<()> {
    +    // Leave fixture locks unheld: foreign-runtime safety must not depend on
    +    // Windows and WSL observing one another's file locks.
    +    for other_os in ["windows", "macos"] {
    +        let home = tempfile::tempdir()?;
    +        let linux_root = arg0_temp_root(home.path(), "linux");
    +        let other_root = arg0_temp_root(home.path(), other_os);
    +        let linux_helper = create_helper_fixture(&linux_root)?;
    +        let other_helper = create_helper_fixture(&other_root)?;
    +
    +        janitor_cleanup(&other_root)?;
    +
    +        assert!(
    +            linux_helper.exists(),
    +            "{other_os} cleanup removed Linux helper"
    +        );
    +        assert!(
    +            !other_helper.exists(),
    +            "stale local helper survived cleanup"
    +        );
    +    }
    +    Ok(())
    +}
    +
    +#[test]
    +fn legacy_cleanup_preserves_namespaced_helpers() -> std::io::Result<()> {
    +    let home = tempfile::tempdir()?;
    +    let legacy_root = home.path().join("tmp").join("arg0");
    +    let platform_root = arg0_temp_root(home.path(), std::env::consts::OS);
    +    let helper = create_helper_fixture(&platform_root)?;
    +
    +    // An older client scans only immediate children of this shared root.
    +    janitor_cleanup(&legacy_root)?;
    +
    +    assert!(helper.exists(), "legacy cleanup removed namespaced helper");
    +    Ok(())
    +}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtool-callsIssues related to tool callingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions