Regression: Windows Desktop + WSL shell stays poisoned after reboot; live app-server holds deleted CODEX_HOME/tmp/arg0 helper path despite unified_exec=false
Summary
This is a regression / worse state than the already-reported Windows Desktop + WSL shell failures in #22185 and #16970.
- #22185 covered
unified_exec=truecausing Windows Desktop + WSL to try toCreateProcessa Linux shell path such as/bin/bash. The workaround wasunified_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=falseis 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 deletedtmp/arg0helper 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=falsein 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=falseis 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
.bathelper 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=falseshould remain a reliable mitigation for the WindowsCreateProcess(/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/arg0helper 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:
- 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. - 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.
- 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.
- 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. - Add a Windows Desktop + WSL release-gate test with:
CODEX_HOMEon%USERPROFILE%\.codex//mnt/c/Users/<user>/.codexunified_exec=false- WSL app-server active
- repeated Desktop restarts/reboots or workspace/thread switches
- verification that shell calls still run and
PATHdoes not point at a deletedcodex-arg0*dir.
Related Issues / PRs
- #22185: earlier
unified_exec=true/ WindowsCreateProcess(/bin/bash)failure - #16970: stale
tmp/arg0helper dir ENOENT until restart - #17570: proposed
.pidsentinel fix for active arg0 helper dirs - #22759: Windows Desktop + WSL config/Codex-home split
- #25216: umbrella release-gate issue for Windows Desktop + WSL
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
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:
unified_exec=falsein both Windows and WSL configs..lockpath, andfdinfoshows a POSIX advisory write lock owned by that live app-server pid.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.
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:Steps used, sanitized:
codex-arg0*directory..lock.Result: normal non-escalated shell calls recovered in the same active thread.
pwd,uname -a, andcommand -v codex-linux-sandboxsucceeded.apply_patchalso recovered and successfully edited a probe file.Two important extra findings:
statand Windows PowerShellGet-Itemreport the helper path missing, but WSLmkdir()returnsEEXIST. Creating the directory from Windows PowerShell withNew-Item -ItemType Directoryworks around that state; then WSL can add symlinks once the path becomes visible.codex.exeafter the rehydrate deletes the repaired WSL helper directory and breaks the active WSL shell again. A new Windows.bathelper dir appears under the same sharedCODEX_HOME/tmp/arg0root. Holding a WSLflockon the recreated.lockdid 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/arg0namespace. 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:
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:
flockon the repaired.lockfile.codex-arg0*helper dir.Then I invoked the Windows-native bundled
codex.exeagain.Result: in both cases, the repaired WSL helper dir was later deleted and a new Windows
.bathelper dir appeared under the same sharedCODEX_HOME/tmp/arg0root. 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.
I supplied a workaround script in #25799 based upon your diagnosis. It is not durable and needs to be run every Codex session.
Adding another sanitized reproduction that appears to match this Desktop + WSL failure family.
Setup:
/home/<user>/Projects/<repo>/home/linuxbrew/.linuxbrew/bin/zsh(Homebrew zsh)echo "$SHELL"->/home/linuxbrew/.linuxbrew/bin/zshgetent passwd "$USER"ends with/home/linuxbrew/.linuxbrew/bin/zshcommand -v zsh->/home/linuxbrew/.linuxbrew/bin/zsh/home/linuxbrew/.linuxbrew/bin/zshis a symlink to../Cellar/zsh/5.9/bin/zshcommand -v bash->/usr/bin/bashapproval_policy = "untrusted"sandbox_mode = "workspace-write"Symptoms from Codex App:
exec_commandstops before the requested command reaches WSL./home/linuxbrew/.linuxbrew/bin/zshis reported as not found./bin/bashand/usr/bin/bashare reported asNo such file or directory.codex-linux-sandboxis not present at a temporary helper path.wsl.exereturnsEPERM.pwd,echo "$SHELL", orgit status --short --branchwas 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 pwdsucceeds 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 --branchsucceeds and shows the expected branch/worktree state.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_commandpath that tries to launch the shell.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
arg0lifecycle problem, not a missing repo/shell/bwrap problem.Current locally proven behavior:
.lock, can recover from WSL/DrvFS oddities where WSLmkdir()reportsEEXISTwhilestat/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:
node_repl.exe -> codex.exe app-server --listen stdio://sidecars can run with sharedCODEX_HOME=%USERPROFILE%\.codex..batarg0 helpers under the same shared%USERPROFILE%\.codex\tmp\arg0root..bathelpers and WSL/Linux symlink helpers are in the same janitor namespace.Current source/PR reading still suggests the durable fix should be one or both of:
tmp/arg0/windows/...andtmp/arg0/wsl/<distro>/..., with janitors sweeping only their own family.batvs symlink), and a safe cross-runtime cleanup rule.Local acceptance gate until upstream fixes this:
%USERPROFILE%\.codex\tmp\arg0contains only WSL/Linux helper dirs during WSL app-server sessions;.bathelpers are allowed only in an isolated Windows runtime home, not the shared Desktop home;node_repl.exe -> codex.exe app-server --listen stdio://sidecar chains are live;/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
arg0by runtime family directly addresses the cross-runtime janitor collision.