Confirmed root cause on Codex 0.147.0: missing inherited Window Station/Desktop access in a Windows OpenSSH Session 0
Confirmed root cause on Codex 0.147.0: missing inherited Window Station/Desktop access in a Windows OpenSSH Session 0
I can reproduce the failure deterministically on Codex CLI 0.147.0 and have completed an A-B-A causal test.
The evidence indicates that the elevated sandbox bootstrap runner is launched with CreateProcessWithLogonW from a non-interactive OpenSSH Session 0, but the selected sandbox account is not granted access to the inherited service Window Station and Desktop.
This causes codex-command-runner-0.147.0.exe to fail during process initialization with 0xC0000142. Codex then waits 15 seconds for the runner pipe and reports a timeout.
All machine-specific names, full SIDs, usernames, and workspace paths below have been redacted.
Environment
- Codex CLI: official standalone stable
0.147.0 - Windows: Windows 11 x64, build
26200(10.0.26200.8457) - Remote transport: Windows OpenSSH
- Client: Codex desktop application launching the remote Codex app-server through SSH
- Host process session: Session 0
- Sandbox configuration:
windows.sandbox = "elevated"windows.sandbox_private_desktop = true- Sandbox provisioning command completed successfully from an elevated PowerShell:
codex sandbox setup --elevated --current-user- exit code
0 - The same official Codex binary and sandbox configuration work from an interactive Session 1.
Original failure
The native elevated sandbox consistently fails over the SSH Session 0 path with:
windows sandbox failed: timed out after 15000ms connecting runner pipe-in
A representative reproduction command is:
$codex = "$env:USERPROFILE\.codex\packages\standalone\current\bin\codex.exe"
& $codex sandbox `
-P :workspace `
-c windows.sandbox=elevated `
-c windows.sandbox_private_desktop=true `
-C $PWD `
"$env:SystemRoot\System32\cmd.exe" `
/d /c "echo SESSION0_BASELINE_OK"
Baseline result:
BASELINE_EXIT=1
windows sandbox failed: timed out after 15000ms connecting runner pipe-in
The timeout is a downstream symptom. Windows records the earlier bootstrap failure in the System event log:
Application Popup
Event ID: 26
Process: codex-command-runner-0.147.0.exe
The application was unable to start correctly (0xc0000142).
0xC0000142 is the process/DLL initialization failure status.
Event timeline
The relevant events occurred in this order:
01:59:24.357 Elevated sandbox launch started
01:59:24.489 Sandbox setup/account refresh started
01:59:24.532 Setup completed, errors=[]
01:59:24.546 Filesystem ACL inspection/refresh started
01:59:24.553 Filesystem ACL step completed
01:59:24.562 command-runner helper cache/path resolved
01:59:24.566 Security event 4624: sandbox account successfully logged on
01:59:24.609 System event 26: command runner failed with 0xC0000142
01:59:39.575 Sandbox logon session ended after the 15-second pipe wait
The active sandbox log was the dated file:
%USERPROFILE%\.codex\.sandbox\sandbox.2026-08-09.log
This shows that provisioning, account lookup, credential logon, filesystem ACL handling, and runner path resolution all completed before the runner initialization failure.
Session 0 Window Station/Desktop state
The SSH-hosted Codex process was running on:
Session ID: 0
Window Station: Service-0x0-<LUID>$
Desktop: Default
Before the causal test, the Window Station and Desktop DACLs contained access entries for:
<interactive-user>
BUILTIN\Administrators
They contained no access entry for either native Codex sandbox account:
CodexSandboxOffline
CodexSandboxOnline
Both sandbox accounts existed and were enabled.
Their direct local group membership was limited to:
CodexSandboxUsers
BUILTIN\Users
The CodexSandboxUsers group contained exactly the two native sandbox accounts.
Account and logon-policy checks
The sandbox accounts effectively received:
SeInteractiveLogonRight
SeNetworkLogonRight
No applicable deny-logon right applied to either account.
The Windows Security log recorded:
Event 4648: explicit credentials used
Event 4624: successful logon
Logon Type: 2
Account: CodexSandboxOffline
There was no corresponding event 4625 authentication failure.
Therefore, this was not caused by an invalid password, a disabled account, missing interactive logon rights, or an explicit deny-logon policy.
A-B-A causal test
I performed a reversible A-B-A experiment against the exact same SSH Session 0 and the exact same official Codex 0.147.0 binary.
A1 — Original DACL
No sandbox-account ACE was present on the current Session 0 Window Station or Desktop.
Result:
BASELINE_EXIT=1
windows sandbox failed: timed out after 15000ms connecting runner pipe-in
B — Add only the missing sandbox-account access
I temporarily added an ACE for the CodexSandboxOffline account SID to the current inherited objects.
No group, user, service, Codex configuration, executable, or filesystem permission was changed.
The masks used were the standard non-interactive Window Station/Desktop masks:
Window Station: 0x000F006E
Desktop: 0x000F00CF
The exact same official sandbox command was then run with a different output marker.
Result:
PROBE_EXIT=0
SESSION0_DACL_CAUSAL_OK
There was:
no runner pipe timeout
no 0xC0000142 event for this launch
no stderr output
The native elevated sandbox therefore completed successfully inside the same SSH Session 0 after only the missing object access was supplied.
Restore
The original binary security descriptors were restored in a finally path.
Before/after SHA-256 checks were identical:
Window Station:
C22FE903AFC19D63F06DB090379CD5C98816F5CC45DCD86A530471BED2BF9365
Desktop:
F78C04E3B3BFC5F6322100CE288EB5678BC45E5576261724843C38E8A33446E1
Verification:
STATION_RESTORED_EXACT=True
DESKTOP_RESTORED_EXACT=True
A2 — Original DACL restored
After exact restoration, the same official command was executed again.
Result:
POST_RESTORE_EXIT=1
windows sandbox failed: timed out after 15000ms connecting runner pipe-in
The observed sequence was therefore:
missing ACE -> failure
temporary minimal ACE -> success
exact DACL restoration -> same failure returns
This demonstrates direct causality rather than correlation.
Interactive Session 1 control test
The same official Codex 0.147.0 executable, sandbox accounts, configuration, and machine succeeded when launched from an interactive Session 1:
SESSION1_NATIVE_OK
exit code 0
The firmware, virtualization state, installed Windows components, sandbox accounts, and Codex files were unchanged. The material difference was the inherited Windows session and GUI-object security context.
Source-code match
In Codex 0.147.0, the elevated runner bootstrap code creates a zeroed STARTUPINFOW, sets only cb, leaves lpDesktop null, and calls CreateProcessWithLogonW directly:
The corresponding current main implementation still follows the same launch sequence:
https://github.com/openai/codex/blob/main/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs
The private desktop is created later by the already-running command runner:
Therefore, changing windows.sandbox_private_desktop cannot fix this bootstrap failure. The runner must start successfully before it can create or use the final private desktop.
Windows API contract
Microsoft documents the relevant responsibility for CreateProcessWithLogonW:
By default,CreateProcessWithLogonWcreates the new process on a noninteractive window station with a desktop that is not visible and cannot receive user input. To enable user interaction with the new process, you must specify the name of the default interactive window station and desktop,winsta0\default, in thelpDesktopmember of theSTARTUPINFOstructure. In addition, before callingCreateProcessWithLogonW, the application must add permission for the specified user account to the specified window station and desktop.
API documentation:
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw
Microsoft also documents the dynamically created non-interactive service Window Station naming and default access model:
https://learn.microsoft.com/en-us/windows/win32/winstation/window-station-and-desktop-creation
Process-to-Window-Station association is documented here:
https://learn.microsoft.com/en-us/windows/win32/winstation/process-connection-to-a-window-station
In this case, lpDesktop is null, so the runner inherits the caller's Session 0 service Window Station/Desktop context. The selected sandbox account has not been granted access to those inherited GUI objects.
Other causes ruled out
The following were checked and did not explain the failure:
- Sandbox provisioning failure
- Missing or disabled sandbox accounts
- Incorrect sandbox account password
- Missing interactive logon right
- An applicable deny-logon policy
- Runner executable missing from cache
- Filesystem read/traverse ACL failure
- AppLocker
- effective policy contained no rule collections
- no AppLocker event matched Codex
- WDAC/Code Integrity
- no Code Integrity event matched
codex.exe,codex-command-runner, or.codex - Firmware virtualization
- Hyper-V
- Windows Sandbox optional component
- WSL/WSL2
- The final Codex private desktop option
Firmware virtualization, Hyper-V, Windows Sandbox, and WSL2 are separate virtualization features. The failing native Codex path had already reached CreateProcessWithLogonW and the Windows GUI-object access boundary.
Why reboot appeared to change the behavior
The Service-0x...$ Window Station is a dynamic object associated with the non-interactive service logon session.
A temporary ACE applied directly to one instance is not a persistent Codex configuration. After reboot or service/session recreation, Windows creates a new service-session Window Station/Desktop using its normal DACL construction.
This explains why a previous temporary repair could work before reboot and disappear afterward.
Suggested upstream fix
The durable fix appears to belong in:
codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs
Before calling CreateProcessWithLogonW, the runner launcher should:
- Resolve the account SID for the selected
sandbox_credsaccount, either Offline or Online. - Open the current inherited Window Station and Desktop.
- Add only the minimal required ACEs for that sandbox account.
- Keep those ACEs valid until the runner has connected its pipe and completed the bootstrap/
SpawnReadyphase. - Remove only the ACEs inserted by that launch.
- Handle concurrent Offline/Online launches with serialization or reference counting so one launch cannot remove another launch's ACE.
- Avoid replacing or restoring the entire DACL in a way that could overwrite unrelated concurrent ACL changes.
The final private desktop should remain enabled by default. These are two separate security layers:
Layer 1: bootstrap runner access to the inherited Session 0 objects
Layer 2: final sandbox command isolation on Codex's private desktop
Fixing Layer 1 should not weaken Layer 2.
Current machine state after testing
The diagnostic ACEs were removed and both original binary security descriptors were restored exactly.
No persistent workaround remains:
no scheduled task
no helper service
no sshd DefaultShell wrapper
no login script
no sandbox-account group change
no Codex executable modification
no persistent Window Station/Desktop ACE
As expected after restoration, the official native elevated sandbox still fails over the SSH Session 0 path and still succeeds in interactive Session 1.
This evidence strongly suggests that issue #27265 is a reproducible caller-side bootstrap ACL defect rather than an unsupported Windows configuration, virtualization problem, or machine-specific policy failure.
2 Comments
Your diagnosis matches the code. In
spawn_runner_transportthe elevated runner is launched with a zeroedSTARTUPINFOW—lpDesktopis never set — and nothing grants the sandbox account access to the caller's window station/desktop beforeCreateProcessWithLogonW(https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs#L340-L365). Interactively, seclogon ACLs WinSta0 for the new logon; on the Session 0 service window station it doesn't, so user32 init fails with0xC0000142exactly as you observed. Notably, the crate'sLaunchDesktop(sandbox_private_desktop) machinery exists but isn't wired into this spawn path at all, so your config setting never applies to the runner process.The misleading error is a second bug: the pipe connect waits the full 15 s on
recv_timeoutwithout also waiting onpi.hProcess, so a child that dies at init still burns the timeout and reportstimed out ... connecting runner pipe-ininstead of the runner's exit code (same family as #38665).Fix outline: (1) explicitly ACL the current window station + desktop for the sandbox SID (or set
si.lpDesktopto aLaunchDesktop-created private desktop) before spawn; (2) race pipe-connect againstWaitForSingleObject(pi.hProcess)and surface0xC0000142directly.Additional confirmation from a separate Windows ARM64 environment: this issue is still reproducible on the current alpha build.
Environment
0.149.0-alpha.426.818.31338(win32-arm64)[windows] sandbox = "elevated"Minimal reproduction
Running even a minimal command such as
Get-Locationthrough the native sandbox consistently ends with:The command runner is spawned and its named
-in/-outpipes are visible, but it never connects. Windows System log recordsApplication Popup, Event ID 26, forcodex-command-runner-0.149.0-alpha.4.exe:The sandbox setup completes without reported errors, both sandbox accounts are enabled, runner source/cached executable hashes match, and no matching Defender, AppLocker, or Code Integrity block was found.
Compatibility checks
windows.sandbox_private_desktop=false: same pipe timeoutwindows.sandbox="unelevated": PowerShell exits with-1073741502(0xC0000142)cmd.exe /d /c cd: succeedsThe same behavior was also present with runner
0.148.0-alpha.21/ extension26.818.22352, so it is not isolated to the latest update.No user name, VM name, workspace path, repository, project code, or secrets are included. No persistent workaround or machine configuration change was left in place. Sanitized logs can be provided if useful.