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
8 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.Additional cross-platform confirmation: this is reproducible in a native Linux VS Code Remote environment, not only Windows Desktop + WSL.
Environment
openai.chatgpt-26.810.41047-linux-x64codex-cli 0.148.0-alpha.9CODEX_HOMEresolves to a GPFS filesystem$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
2026-08-17T16:19:02Z2026-08-17T16:19:08Zcodex-arg03UKYsn, but that directory was absent..../codex-arg03UKYsn/.lock (deleted)./bin/bash,/bin/sh,/usr/bin/bash, and/usr/bin/shall failed before the requested command started:Failed to create unified exec process: No such file or directory (os error 2)Reloading the remote extension host created a new helper directory and temporarily restored managed execution.
Occurrence 2
2026-08-17T23:10:05Zcodex-arg02wA5kGwas also absent..../codex-arg02wA5kG/.lock (deleted).fdinfoshowed that the live app server still held an advisory writeFLOCKon the deleted inode.pwdfailed with the same unified-exec ENOENT.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:
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.