Windows: sandbox setup_marker.json written without an ACE for the invoking user in non-interactive exec, so setup repeats forever (orchestrator_helper_incomplete)

Resolved 💬 4 comments Opened Aug 27, 2026 by JoshFunnell Closed Aug 28, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

On Windows, when codex exec runs non-interactively with a CODEX_HOME that has no existing sandbox state, the sandbox setup helper writes .sandbox\setup_marker.json with an ACL that contains no entry for the invoking user. The marker therefore cannot be read back on any later run, so every subsequent invocation reports the marker as missing/incompatible, re-runs full sandbox setup, and the exec fails before the command runs:

exec_command failed for `"C:\Program Files\PowerShell\7\pwsh.exe" -NoProfile -Command "..."`:
CreateProcess { message: "Rejected(\"Failed to create unified exec process:
orchestrator_helper_incomplete: setup helper exited successfully before setup completed\")" }

The same machine runs the same command successfully from the interactive app, because the interactive setup writes a marker the user can read. That asymmetry appears to be the whole defect.

Environment

  • Windows 11
  • codex-cli installed via npm i -g @openai/codex
  • Reproduced on 0.146.0-alpha.9.2, 0.149.1 and 0.150.1
  • [windows] sandbox = "elevated", sandbox_mode = "workspace-write"
  • Non-admin user account; UAC prompt for codex-windows-sandbox-setup.exe accepted each time

Reproduction

  1. Point CODEX_HOME at a directory with no existing .sandbox state:

``
set CODEX_HOME=D:\work\codex_home\job-0001
``

  1. Run a trivial exec non-interactively (stdin-fed prompt, no TTY):

``
codex exec --skip-git-repo-check -s workspace-write -c approval_policy=never --ephemeral -
`
with a prompt asking the model to run one shell command, e.g.
cmd /c echo OK`.

  1. Accept the UAC prompt. The run fails with orchestrator_helper_incomplete.
  2. Run the identical command again against the same CODEX_HOME. Setup runs again and it fails again — indefinitely.

Evidence

%CODEX_HOME%\.sandbox\sandbox.<date>.log shows setup starting, completing its work, and finishing:

codex.exe] sandbox setup required: sandbox setup marker missing or incompatible
ensuring sandbox users offline=CodexSandboxOffline online=CodexSandboxOnline
firewall rule configured name=codex_sandbox_offline_block_loopback_udp ...
WFP setup succeeded for CodexSandboxOffline with 12 installed filters
granting write ACE to <temp dir> for sandbox group and capability SID
applied deny ACE to protect <workdir>\.git
codex-windows-sandbox-setup.exe] setup binary completed

Setup takes roughly 20 seconds, and the model's shell call is attempted while it is still running — which matches the wording "setup helper exited successfully before setup completed".

The marker it leaves behind is unreadable to the user who launched it:

> icacls "%CODEX_HOME%\.sandbox\setup_marker.json"
setup_marker.json: Access is denied.
Successfully processed 0 files; Failed processing 1 files

Compare the marker written by the interactive app, same machine, same user:

> icacls "C:\Users\<user>\.codex\.sandbox\setup_marker.json"
setup_marker.json  NT AUTHORITY\SYSTEM:(F)
                   BUILTIN\Administrators:(F)
                   <MACHINE>\<user>:(F)

Granting the missing read ACE by hand changes the failure message on the next run from
sandbox setup marker missing or incompatible to
sandbox users missing or incompatible with marker version,
i.e. the marker then parses but no longer matches the sandbox accounts — consistent with setup having rotated those accounts on the run that wrote the unreadable marker. The grant does not survive, because the next setup writes a fresh marker with the same restrictive ACL.

Across four days of logs on this machine, junction: failed to create <profile>\.codex\.sandbox\cwd: Access is denied. (os error 5) appears 83 times and never once succeeds; the sandbox accounts have no loaded user profile, so that path resolves under C:\Users\Default, which is read-only for non-admins. This may be a second symptom of the same "setup runs as a principal whose view of the user differs" problem.

Expected

A marker written by sandbox setup should be readable by the user who invoked codex, so that a second run can reuse the completed setup instead of repeating it.

Actual

The marker is unreadable to that user, so setup repeats on every invocation and non-interactive exec can never obtain a shell.

Ruled out during investigation

Each of these was tested and made no difference: PowerShell 7 from the Store/MSIX vs. the MSI build (byte-identical error text); a full reboot; upgrading 0.149.1 → 0.150.1; downgrading to 0.146.0-alpha.9.2; third-party antivirus disabled; and closing the interactive app entirely, so nothing else was competing for the machine-wide sandbox accounts.

Impact

Interactive use is unaffected. Automated/headless codex exec — CI, scheduled jobs, any orchestration that gives each run its own CODEX_HOME — cannot use the shell tool at all on Windows.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 22 hours ago

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

  • #40217

Powered by Codex Action

JoshFunnell · 20 hours ago

Related, and likely the same root cause reached by a different entry point: #40217 reports the identical sandbox setup marker missing or incompatible / orchestrator_helper_incomplete cycle, there from the VS Code extension's app-server path rather than from codex exec. The common factor in both reports is sandbox setup being invoked from a non-interactive host process.

One thing worth adding for whoever picks this up: the workaround in #40217 — granting the invoking user Full Control on setup_marker.json by hand — is not a general one. It repairs a marker at a fixed path, so it can help a single long-lived CODEX_HOME. Any orchestration that gives each run its own CODEX_HOME has no stable marker to repair, and the next setup writes a fresh marker with the same restrictive ACL regardless.

JoshFunnell · 11 hours ago

Closing this — the cause was on my side, not in codex

I found the real cause and it is my harness, not this project. Posting the full
mechanism rather than deleting the issue, because the report above is wrong in a
specific and reproducible way and anyone who finds it by searching the same error
string deserves the correction.

What actually happened

My launcher builds the child environment for codex exec from an explicit
allowlist (a deliberate no-secret-inheritance measure). That allowlist did not
include USERNAME.

codex resolves the invoking end-user from the environment — its own strings
include failed to determine current user from environment: ,
--user or --current-user is required, and
--codex-home is required with --user. With USERNAME absent, the elevated
setup helper stamped the end-user ACE on <CODEX_HOME>\.sandbox as:

BUILTIN\Administrators:(OI)(CI)(RX,W)

where the same slot in a normally-launched home carries:

<MACHINE>\<user>:(OI)(CI)(RX,W)

Same slot, same access mask, same inheritance flags — only the trustee differs.

My account is in Administrators, but a non-elevated token carries that SID as
Group used for deny only (whoami /groups, Medium Mandatory Level), and a
deny-only SID never grants access. So setup_marker.json was unreadable to the
very orchestrator that had just requested it → sandbox setup marker missing or
incompatible
→ setup re-runs → exits 0 with nothing to do →
orchestrator_helper_incomplete.

Fix and acceptance

Adding USERNAME (and USERDOMAIN) to my launcher's environment allowlist fixes
it. Measured, not inferred: a canary job returned SHELL_CANARY_OK, against a
byte-identical prompt run 30 minutes earlier that returned
orchestrator_helper_incomplete.

Corrections to the report above

  • "non-interactive exec" is not the trigger. A missing USERNAME in the

child environment is. Non-interactive execution correlated with it only because
that is how my harness spawns codex.

  • **"the same machine runs the same command successfully from the interactive

app" — not the same binary.** The interactive app here is the MSIX
OpenAI.CodexBeta build; the failing lane is the npm CLI at
AppData\Roaming\npm\node_modules\@openai\codex. The asymmetry I described was
real but I attributed it to interactive-vs-headless when the environment was
the actual difference.

  • "Reproduced on 0.146.0-alpha.9.2, 0.149.1 and 0.150.1" — all three were run

through the same broken harness, so that list is not evidence about the
versions.

  • My cross-link to #40217 should be disregarded. I speculated it was "likely

the same root cause reached by a different entry point". I have no evidence for
that, and since my own case turned out to be environmental, that guess should
not be allowed to steer someone else's report. Treating it as unrelated until
someone shows otherwise.

  • **The junction: failed to create ...\.codex\.sandbox\cwd: Access is denied

observation is unexplained.** It may or may not share this cause; I did not
test it and am not claiming it.

Two things you may still want, offered without any claim that they are bugs

  1. When the current user cannot be resolved from the environment, setup stamps an

ACE the invoking token provably cannot use and continues. Failing loudly at
that point — the binary already has the string for it — would have turned four
days of investigation into one line of output.

  1. `orchestrator_helper_incomplete: setup helper exited successfully before setup

completed` is accurate but points away from the cause. "Setup marker written
but not readable by <user>" would name it.

One diagnostic note in case it saves someone else the time: with --ephemeral,
the per-job .sandbox directory is removed at exit (its mtime matched the job's
end time to the millisecond). Every post-mortem I ran found an empty directory
and concluded setup had never started, which is why the ACL cause kept getting
refuted by its own evidence being deleted. The marker is only observable while
the job is still running.

Apologies for the noise, and thanks for the tracker.

JoshFunnell · 11 hours ago

Closing as not a codex defect -- root cause was my own launcher stripping USERNAME from the child environment. Full mechanism, fix and acceptance measurement in the comment above.