Regression: Windows Desktop + WSL shell stays poisoned after reboot; live app-server holds deleted CODEX_HOME/tmp/arg0 helper path despite unified_exec=false

Open 💬 8 comments Opened May 30, 2026 by MisterRound
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

This is a regression / worse state than the already-reported Windows Desktop + WSL shell failures in #22185 and #16970.

  • #22185 covered unified_exec=true causing Windows Desktop + WSL to try to CreateProcess a Linux shell path such as /bin/bash. The workaround was unified_exec=false.
  • #16970 covered stale CODEX_HOME/tmp/arg0/codex-arg0* helper paths where restarting Desktop generally regenerated a working helper directory.
  • This report is worse: unified_exec=false is already set in both the Windows Desktop Codex home and the WSL Codex home, the machine was fully rebooted more than once, and a fresh Desktop + WSL app-server session can still reach a poisoned state where the active shell/tool runtime is pinned to a deleted tmp/arg0 helper directory.

In other words, reboot/relaunch is no longer a reliable recovery for this failure class.

Environment

  • Windows Desktop package: OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0
  • Desktop release string in logs: 26.527.31326
  • Electron runtime: 42.1.0
  • Active WSL app-server / hashed WSL agent: codex-cli 0.135.0-alpha.1
  • Stable WSL agent path still present: codex-cli 0.130.0-alpha.5
  • Platform: Windows 11 + WSL2 Ubuntu
  • WSL kernel observed: 6.6.114.1-microsoft-standard-WSL2
  • Desktop CODEX_HOME: %USERPROFILE%\.codex, seen from WSL as /mnt/c/Users/<user>/.codex
  • Shell in the affected Desktop session: /bin/bash
  • unified_exec=false in both:
  • %USERPROFILE%\.codex\config.toml
  • <wsl-home>/.codex/config.toml

Actual Behavior

Normal shell/tool calls fail before the requested command starts:

execution error: Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })

The workspace path is not the problem. The active session's PATH contains a stale helper directory:

/mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV

That directory no longer exists:

stat: cannot statx '/mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV': No such file or directory

However, the live WSL app-server still holds an fd to the stale helper lock path:

/proc/<app-server-pid>/fd/3 -> /mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV/.lock

And fdinfo shows the live process still has a POSIX advisory write lock on that old lock inode:

lock: 1: POSIX  ADVISORY  WRITE <app-server-pid> ...

Later, a new codex-arg0* directory can appear under the same tmp/arg0 root with valid Linux symlinks:

apply_patch -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
applypatch -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
codex-execve-wrapper -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
codex-linux-sandbox -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex

But this does not repair the active thread, because the active thread/app-server state remains pinned to the deleted codex-arg0UXHjoV helper path.

Why This Is Not Just a Duplicate

This is related to #16970 and #22185, but it is not the same recovery state:

  • unified_exec=false is already applied in every relevant config.
  • A full OS reboot was attempted more than once.
  • The failure still reappears in the current Desktop build family.
  • The live app-server can retain an fd/lock for a helper path that is already gone from the filesystem.
  • Creating a newer helper directory does not rebind the active shell/tool runtime.

So the old practical workaround, "restart/reboot Desktop so a fresh helper dir is created," is no longer sufficient.

Source-Level Findings

I checked current origin/main of openai/codex at:

8acaec73b6a227ae9375064e910ecf86f2620ec0

Relevant current behavior in codex-rs/arg0/src/lib.rs:

  • The helper root is shared under find_codex_home()/tmp/arg0.
  • Unix creates helper symlinks in that root: apply_patch, applypatch, codex-execve-wrapper, codex-linux-sandbox.
  • Windows creates .bat helper shims in the same root.
  • Cleanup is lock-only: janitor_cleanup() attempts to lock each child dir and deletes it if locking succeeds.

This is fragile in Windows Desktop + WSL because the Desktop home is shared across a Windows process family and a WSL/Linux process family, and in this setup CODEX_HOME is on DrvFS under /mnt/c.

PR #17570 adds a .pid owner sentinel and appears to address the Linux-side stale-helper case, but I do not see that commit on origin/main. Also, as currently written on that branch, the owner PID liveness check returns false on non-Linux. That means it may not fully protect a WSL-owned helper directory if a Windows-native Codex process is janitoring the same shared %USERPROFILE%\.codex\tmp\arg0 tree.

Related Runtime Evidence

The same Desktop logs show mixed runtime surfaces in the same launch:

stdio_transport_spawned ... executablePath=%USERPROFILE%\.codex\bin\wsl\<hash>\codex ... spawnCommand=wsl.exe
Current reported app-server version: currentVersion=0.135.0-alpha.1

But Browser/Computer Use runtime selection in the same session selected Windows-native paths:

browser_use_runtime_paths_selected codexCliPath=%LOCALAPPDATA%\OpenAI\Codex\bin\<hash>\codex.exe ... platform=win32
computer-use native pipe startup failed ... "Windows Computer Use helper paths are unavailable"

I am not claiming the Browser/Computer Use plugin failure is the root cause of the shell failure. I am including it because it shows the same Desktop session crossing Windows-native and WSL-native runtime/cache surfaces while sharing the same Codex home.

Expected Behavior

For Windows Desktop + WSL sessions:

  • unified_exec=false should remain a reliable mitigation for the Windows CreateProcess(/bin/bash) failure class.
  • Reboot/relaunch should not deterministically return to a poisoned helper state.
  • A live app-server should not have its active tmp/arg0 helper directory deleted while it still uses paths inside it.
  • If a cached codex-linux-sandbox / arg0 helper path returns ENOENT, the runtime should rehydrate/rebind helper paths instead of leaving the thread permanently broken.

Suggested Fix Direction

One or more of:

  1. Namespace arg0 helper dirs by runtime family/platform/distro, for example separate Windows-native helpers from WSL/Linux helpers instead of sharing CODEX_HOME/tmp/arg0.
  2. Add a helper manifest containing platform, helper kind, owner PID, and WSL distro/runtime identity; janitors should only clean dirs from their own runtime family.
  3. Make the active-helper ownership check cross-runtime safe. If a Windows process cannot verify a WSL PID, it should not treat that helper dir as safe to delete.
  4. On ENOENT for cached codex_linux_sandbox_exe / arg0 helper paths, rebind once from the live app-server's current helper guard or recreate a helper dir, then retry.
  5. Add a Windows Desktop + WSL release-gate test with:
  • CODEX_HOME on %USERPROFILE%\.codex / /mnt/c/Users/<user>/.codex
  • unified_exec=false
  • WSL app-server active
  • repeated Desktop restarts/reboots or workspace/thread switches
  • verification that shell calls still run and PATH does not point at a deleted codex-arg0* dir.

Related Issues / PRs

  • #22185: earlier unified_exec=true / Windows CreateProcess(/bin/bash) failure
  • #16970: stale tmp/arg0 helper dir ENOENT until restart
  • #17570: proposed .pid sentinel fix for active arg0 helper dirs
  • #22759: Windows Desktop + WSL config/Codex-home split
  • #25216: umbrella release-gate issue for Windows Desktop + WSL

View original on GitHub ↗

8 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #25280

Powered by Codex Action

MisterRound · 2 months ago

I reviewed the duplicate bot suggestion, #25280. It is related and probably the closest existing report, but I do not think this issue should be closed as a straight duplicate.

The difference I am trying to isolate here is the regression state and live ownership evidence:

  • This repro is specifically after the older mitigation is already in place: unified_exec=false in both Windows and WSL configs.
  • Full reboot/relaunch was already attempted multiple times, so this is worse than the original "restart regenerates a good helper dir" state from #16970.
  • The live WSL app-server still has an fd to the deleted helper dir's .lock path, and fdinfo shows a POSIX advisory write lock owned by that live app-server pid.
  • Later/newer codex-arg0* helper dirs can exist, but the active thread remains pinned to the deleted helper path.

#25280 is broader: it also tracks elevated Windows sandbox setup/refresh failures and Windows-local repo sandbox behavior. This issue is intended to be the narrower WSL/Desktop arg0 lifecycle regression with the fd/lock evidence and the "reboot no longer recovers" behavior.

MisterRound · 2 months ago

Additional live workaround/root-cause isolation from the same affected session.

I tested a non-destructive live rehydrate of the exact stale helper path referenced by PATH:

/mnt/c/Users/<user>/.codex/tmp/arg0/codex-arg0UXHjoV

Steps used, sanitized:

  1. Recreate the exact missing codex-arg0* directory.
  2. Recreate .lock.
  3. Add Linux symlinks:
apply_patch -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
applypatch -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
codex-execve-wrapper -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex
codex-linux-sandbox -> /mnt/c/Users/<user>/.codex/bin/wsl/<hash>/codex

Result: normal non-escalated shell calls recovered in the same active thread. pwd, uname -a, and command -v codex-linux-sandbox succeeded. apply_patch also recovered and successfully edited a probe file.

Two important extra findings:

  1. WSL/DrvFS can get into an inconsistent recreate state for the deleted helper path. stat and Windows PowerShell Get-Item report the helper path missing, but WSL mkdir() returns EEXIST. Creating the directory from Windows PowerShell with New-Item -ItemType Directory works around that state; then WSL can add symlinks once the path becomes visible.
  1. Running the Windows-native bundled codex.exe after the rehydrate deletes the repaired WSL helper directory and breaks the active WSL shell again. A new Windows .bat helper dir appears under the same shared CODEX_HOME/tmp/arg0 root. Holding a WSL flock on the recreated .lock did not protect the directory from the Windows-native cleanup path.

That makes the mixed-runtime root cause much more concrete: a Windows-native Codex process and a WSL/Linux Codex app-server share the same %USERPROFILE%/.codex/tmp/arg0 namespace. The Windows-native arg0 janitor can delete the WSL helper dir that the live WSL app-server/thread still has cached, and WSL advisory locking does not reliably protect it.

This also changes the workaround guidance:

  • Rehydrating the exact stale helper dir can recover the current thread temporarily.
  • It is not durable if Windows-native Codex/plugin/browser-use paths run afterward.
  • The durable product fix needs platform/runtime namespacing or a janitor rule that never deletes helper dirs from another runtime family.
MisterRound · 2 months ago

One more workaround test result: holding the repaired helper dir open from WSL does not prevent the Windows-native cleanup.

I tried two protection variants after rehydrating the exact stale WSL helper dir:

  1. Start a long-running WSL process holding an exclusive flock on the repaired .lock file.
  2. Start a long-running WSL process with its current working directory inside the repaired codex-arg0* helper dir.

Then I invoked the Windows-native bundled codex.exe again.

Result: in both cases, the repaired WSL helper dir was later deleted and a new Windows .bat helper dir appeared under the same shared CODEX_HOME/tmp/arg0 root. Normal WSL shell/tool calls broke again.

So this is not just "WSL did not hold the right lock." Handle-based local protection from WSL is not a reliable workaround against the Windows-native arg0 cleanup path. The practical local workaround is only to rehydrate after the break, and then avoid triggering Windows-native Codex/plugin/browser-use paths. The product fix needs to stop Windows and WSL runtimes from janitoring each other's helper dirs in the first place.

poroburu · 2 months ago

I supplied a workaround script in #25799 based upon your diagnosis. It is not durable and needs to be run every Codex session.

kosei-w90607 · 2 months ago

Adding another sanitized reproduction that appears to match this Desktop + WSL failure family.

Setup:

  • Windows 11 + WSL2
  • Codex App settings:
  • Default open location: WSL
  • Agent environment: Windows Subsystem for Linux
  • Integrated terminal shell: WSL
  • WSL distro: Ubuntu-22.04
  • Workspace: WSL-hosted git repository under /home/<user>/Projects/<repo>
  • WSL login shell: /home/linuxbrew/.linuxbrew/bin/zsh (Homebrew zsh)
  • Direct WSL terminal can resolve the shell and bash:
  • echo "$SHELL" -> /home/linuxbrew/.linuxbrew/bin/zsh
  • getent passwd "$USER" ends with /home/linuxbrew/.linuxbrew/bin/zsh
  • command -v zsh -> /home/linuxbrew/.linuxbrew/bin/zsh
  • /home/linuxbrew/.linuxbrew/bin/zsh is a symlink to ../Cellar/zsh/5.9/bin/zsh
  • command -v bash -> /usr/bin/bash
  • Relevant config currently observed:
  • approval_policy = "untrusted"
  • sandbox_mode = "workspace-write"

Symptoms from Codex App:

  • exec_command stops before the requested command reaches WSL.
  • The default shell /home/linuxbrew/.linuxbrew/bin/zsh is reported as not found.
  • /bin/bash and /usr/bin/bash are reported as No such file or directory.
  • PTY execution fails because codex-linux-sandbox is not present at a temporary helper path.
  • Node-side attempt to spawn wsl.exe returns EPERM.
  • The app terminal snapshot still shows a WSL cwd and git branch, but requested command output such as pwd, echo "$SHELL", or git status --short --branch was not obtained because execution fails before the command starts.

This makes it look less like a repository or WSL shell configuration issue and more like the Desktop WSL command runner / sandbox helper failing before entering WSL. The direct WSL shell is healthy, including Homebrew zsh and /usr/bin/bash.

Update after further testing:

  • wsl.exe -d Ubuntu-22.04 --cd /home/<user>/Projects/<repo> --exec pwd succeeds and returns the expected WSL repo path.
  • wsl.exe ... --exec sh -lc 'echo "$SHELL"' succeeds and returns /home/linuxbrew/.linuxbrew/bin/zsh.
  • wsl.exe ... --exec git status --short --branch succeeds and shows the expected branch/worktree state.
  • Normal Codex Desktop shell execution still fails before reaching WSL because it appears to try launching the WSL shell path from the Windows side, effectively a Windows-native CreateProcess("/home/linuxbrew/.linuxbrew/bin/zsh", ...) style failure.

So the WSL distro, repo, git, and Linuxbrew zsh are healthy when invoked through explicit wsl.exe --exec. The failing path appears to be the Desktop normal exec path selecting or falling back to a Windows-native process launch for a Linux shell path instead of wrapping the shell command through WSL.

One caveat: a Git status signal from Desktop should not necessarily be treated as proof that normal shell execution is healthy. Git/repository status may be coming from a Desktop/plugin/MCP/internal repository metadata path rather than the same exec_command path that tries to launch the shell.

MisterRound · 2 months ago

Follow-up with the latest sanitized local findings from the same Windows Desktop + WSL investigation.

The original diagnosis still holds, but the current reproduction is sharper: this is a mixed-runtime arg0 lifecycle problem, not a missing repo/shell/bwrap problem.

Current locally proven behavior:

  • A WSL-backed Desktop app-server can be pinned to a helper path like:
%USERPROFILE%\.codex\tmp\arg0\codex-arg0<hash>
  • When that exact helper dir disappears, shell/tool calls fail before the requested command starts:
Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })
  • Recreating the exact stale helper dir with Linux symlinks restores the live thread temporarily:
apply_patch -> %USERPROFILE%\.codex\bin\wsl\<hash>\codex
applypatch -> %USERPROFILE%\.codex\bin\wsl\<hash>\codex
codex-execve-wrapper -> %USERPROFILE%\.codex\bin\wsl\<hash>\codex
codex-linux-sandbox -> %USERPROFILE%\.codex\bin\wsl\<hash>\codex
  • The local recovery script now preserves an existing .lock, can recover from WSL/DrvFS oddities where WSL mkdir() reports EEXIST while stat/Windows APIs report the path missing, and can fall back to the live app-server lock fd when visible.

The additional finding is that Computer Use/browser-use sidecars can make this worse:

  • Product-generated Windows-native node_repl.exe -> codex.exe app-server --listen stdio:// sidecars can run with shared CODEX_HOME=%USERPROFILE%\.codex.
  • Those Windows-native sidecars create .bat arg0 helpers under the same shared %USERPROFILE%\.codex\tmp\arg0 root.
  • When that happens in a WSL-backed Desktop session, shared-root Windows .bat helpers and WSL/Linux symlink helpers are in the same janitor namespace.
  • Cleanup/recovery that only creates a new helper dir is not durable if the active app-server remains pinned to the deleted helper path or if another runtime family can janitor the shared root again.

Current source/PR reading still suggests the durable fix should be one or both of:

  1. Runtime-family helper namespacing, e.g. tmp/arg0/windows/... and tmp/arg0/wsl/<distro>/..., with janitors sweeping only their own family.
  2. A helper manifest with runtime family, owner PID namespace, creator executable, helper kind (bat vs symlink), and a safe cross-runtime cleanup rule.
  3. A one-shot rehydrate/rebind-on-ENOENT path for cached helper executable paths before surfacing shell failure.

Local acceptance gate until upstream fixes this:

  • shared %USERPROFILE%\.codex\tmp\arg0 contains only WSL/Linux helper dirs during WSL app-server sessions;
  • Windows .bat helpers are allowed only in an isolated Windows runtime home, not the shared Desktop home;
  • no bare/shared-home node_repl.exe -> codex.exe app-server --listen stdio:// sidecar chains are live;
  • if pre-exec ENOENT happens, rehydrate the exact stale helper dir before trying /bin/bash -lc, repo-path checks, or bwrap debugging.

This is also why #26985 looks directionally right: moving runtime-heavy WSL temp/cache state out of the shared Windows-backed home and/or namespacing arg0 by runtime family directly addresses the cross-runtime janitor collision.

rpc1990 · 10 days ago

Additional cross-platform confirmation: this is reproducible in a native Linux VS Code Remote environment, not only Windows Desktop + WSL.

Environment

  • VS Code Remote extension: openai.chatgpt-26.810.41047-linux-x64
  • Bundled runtime: codex-cli 0.148.0-alpha.9
  • Platform: Linux x86_64
  • CODEX_HOME resolves to a GPFS filesystem
  • Affected path class: $CODEX_HOME/tmp/arg0/codex-arg0*

Observed twice on 2026-08-17

The same failure occurred in two successive VS Code Codex execution hosts.

Occurrence 1

  • Last confirmed managed-sandbox success: 2026-08-17T16:19:02Z
  • First host-wide failures: 2026-08-17T16:19:08Z
  • The live app server still referenced codex-arg03UKYsn, but that directory was absent.
  • App-server FD 3 pointed to .../codex-arg03UKYsn/.lock (deleted).
  • /bin/bash, /bin/sh, /usr/bin/bash, and /usr/bin/sh all failed before the requested command started:

Failed to create unified exec process: No such file or directory (os error 2)

  • A fresh standalone sandbox using the same bundled Codex binary passed.

Reloading the remote extension host created a new helper directory and temporarily restored managed execution.

Occurrence 2

  • First logged failure: 2026-08-17T23:10:05Z
  • The replacement app server remained alive, but its new helper directory codex-arg02wA5kG was also absent.
  • App-server FD 3 again pointed to .../codex-arg02wA5kG/.lock (deleted).
  • fdinfo showed that the live app server still held an advisory write FLOCK on the deleted inode.
  • Even a minimal managed pwd failed with the same unified-exec ENOENT.
  • Execution outside the affected managed sandbox remained healthy.

This recurrence after the extension-host reload is strong evidence that the issue is not limited to stale state inherited from one old process. It also broadens the affected environment beyond the Windows/WSL shared-home case: an active native-Linux app server on GPFS can lose its own helper directory while retaining the deleted lock inode.

The deletion actor is still unknown. No evidence implicated the shell binaries, cwd, project, Bubblewrap, user namespaces, or process/file-descriptor exhaustion.

A robust fix likely needs both:

  1. cleanup protection that does not treat an active helper directory as unused when filesystem locking/liveness evidence is unreliable; and
  2. one-time helper rehydration/rebinding when a cached arg0 helper path returns ENOENT.

I preserved bounded redacted excerpts and source hashes locally. Raw session transcripts and logs are not attached because they contain private conversation and project data, but I can provide additional sanitized diagnostics or use a secure upload channel if maintainers request them.