Codex Desktop on Windows: `.git/FETCH_HEAD: Permission denied` from sandbox-induced orphan-SID Deny ACEs
Environment
- Codex Desktop:
26.429.3425.0(x64, Microsoft Store / WindowsApps install) - OS: Windows 11 Pro 26200
- Repo: fresh
git cloneof a public GitHub repo intoC:\Repos\<repo>\(path outside any OneDrive scope) - Account: standard local user, not Administrator
- Codex CLI 0.128.0 also installed on the same machine; not used for this reproduction
Summary
git pull --ff-only from inside Codex Desktop fails with error: cannot open '.git/FETCH_HEAD': Permission denied. From a normal PowerShell session as the same user against the same path, git pull --ff-only succeeds.
After Codex Desktop's first contact with the repo, host-side Get-Acl .git shows two new explicit Deny ACEs against an "orphan" SID — orphan from the host's IdentityNotMappedException POV but evidently resolving inside the Codex Desktop sandbox to the sandbox's effective principal, since that's the only thing that explains why Codex's own writes inside .git are blocked while host writes succeed.
Reproduction
- Fresh
git cloneinto a path outside any OneDrive Known Folder Move scope. Confirm the freshly-cloned.gitcarries zero explicit Deny ACEs and no orphan SIDs (icacls <repo>\.gitshould show only inheritedBUILTIN\Administrators (F),NT AUTHORITY\SYSTEM (F),BUILTIN\Users (RX),NT AUTHORITY\Authenticated Users (M)). - Open a fresh Codex Desktop session pointed at the clone.
- Have Codex run
git pull --ff-only. - Observe the failure inside Codex:
error: cannot open '.git/FETCH_HEAD': Permission denied. - Re-run
icacls <repo>\.gitfrom a normal host PowerShell session. Two new explicit Deny ACEs are present:
````
<orphan-SID>:(DENY)(W,D,Rc,DC)
<orphan-SID>:(OI)(CI)(IO)(DENY)(W,D,Rc,GW,DC)
The (OI)(CI)(IO) ACE propagates onto every newly-created child of .git, which is what blocks FETCH_HEAD write on the next pull attempt.
Mechanism (hypothesis)
The "orphan" SID has the same shape as a normal machine SID (S-1-5-21-<machine>-<RID>) but its machine prefix matches no SID issuer registered with this Windows host's LSA, so IdentityReference.Translate([NTAccount]) throws IdentityNotMappedException. From the host's POV the Deny is a no-op — that matches the observation that host PowerShell Git operations succeed through these ACEs.
From the Codex Desktop sandbox's POV, however, the same SID resolves to the sandbox's effective principal — otherwise the Deny would be a no-op there too. So Codex Desktop is locking itself out of .git on every clone it touches.
Confirming side-observation: on first contact with a workspace root, Codex Desktop adds two Allow ACEs at the workspace root level — one for the documented CodexSandboxUsers group (expected), and one for a different orphan SID. The Allow case is harmless (Allow for an unmappable SID is a no-op from host POV). The Deny case is the Git breakage.
This appears to be the Windows-native-sandbox manifestation of the same design pattern documented for the Linux bubblewrap path in #14338 (.git/FETCH_HEAD: Read-only file system because bubblewrap mounts .git read-only after writable roots are applied) and reported on the VS Code extension surface in #9341 (.git/index.lock: Permission denied from the same Windows sandbox layer).
Suggested triage
A writable-gitdir opt-in along the lines requested in #14338 would address the underlying design constraint cross-platform.
In the meantime, the Windows sandbox lockout produces a generic "Permission denied" with no in-sandbox indication that the Codex sandbox is the cause. #9341 covered that visibility ask for the VS Code extension surface; the same applies to Codex Desktop standalone. A persistent, in-app indicator that the experimental Windows sandbox is active and that Git operations against .git inside the workspace will fail would prevent users from spending hours diagnosing this as a OneDrive / antivirus / NTFS ownership issue (which is exactly the path that led to filing this report).
Workaround
#9341's reporter confirmed that experimental_windows_sandbox = false in ~/.codex/config.toml resolved their .git/index.lock failure. We have not yet tested this against the FETCH_HEAD reproduction locally; planning to test on a disposable clone next and will follow up here.
Operational workaround in use: never let Codex Desktop run Git operations on this repo. Host PowerShell as the standard user runs all Git; Codex Desktop is restricted to non-Git file edits. That works because the orphan-SID Deny ACEs are no-ops from host POV.
Related issues
- #9341 — VS Code extension surface,
.git/index.lockfailure, same Windows sandbox cause - #14338 — Linux bubblewrap surface, same
.git/FETCH_HEADsymptom, request for writable-gitdir opt-in - #12343 — sandbox creates
CodexSandbox*groups; can leave stale SIDs after uninstall - #13378 — sandbox ACL setup leaves persistent permission pollution
- #15310 — desktop automations falling back to workspace-write sandbox regardless of config
6 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Follow-up after testing on a disposable clone outside any OneDrive scope (
C:\acl-test\Git-Obsidian-backup).TL;DR
[features].experimental_windows_sandbox = false) is no longer functional.codex features listin CLI0.128.0reports bothexperimental_windows_sandboxandelevated_windows_sandboxasremoved. Setting them in[features]is a no-op.[windows].sandbox, which accepts onlyelevated(default) orunelevated— there is no off/disabled/none value. Setting any other value fails at config-load:unknown variant '<value>', expected 'elevated' or 'unelevated'.elevated,unelevated) and Codex CLI inworkspace-writemode all reproduceerror: cannot open '.git/FETCH_HEAD': Permission deniedon a fresh clone outside OneDrive scope. The sandbox layer is the cause — not Git, not NTFS, not the repo, not any environmental factor.codex exec --dangerously-bypass-approvals-and-sandbox -C <clone> "git pull --ff-only"succeeds with exit 0. That is the cleanest possible isolation of the sandbox as the operative factor.elevated: writes orphan-SID Deny ACEs on.git. Yes (host-visible).unelevated: same FETCH_HEAD failure, but no Deny ACEs written. The unelevated mode enforces via restricted-token semantics, not via DACL changes.workspace-write: writes orphan-SID Deny ACEs (with a fresh per-session orphan SID, different from the Desktop one).Detail
Test environment
Same as the original report: Codex Desktop
26.429.3425.0(x64, Microsoft Store / WindowsApps install) + Codex CLI0.128.0, Windows 11 Pro 26200, standard local user. Disposable clone outside OneDrive scope so any orphan-SID ACE that appears is causally attributable to Codex.Test 1 —
[windows] sandbox = "unelevated"Procedure: edit
~/.codex/config.tomlto set[windows] sandbox = "unelevated", fully exit Codex Desktop (kill allCodexprocesses), relaunch Codex Desktop, point at the disposable clone, rungit pull --ff-onlyfrom inside Codex.Result: identical
error: cannot open '.git/FETCH_HEAD': Permission deniedfailure as the elevated mode. But host-sideGet-Acl .gitshowed zero new explicit Deny ACEs — the DACL was byte-identical to the clean-baseline state from immediately aftergit clone.Implication: the unelevated variant of the Windows restricted-token sandbox is "cleaner" in that it doesn't pollute host-visible ACLs, but it doesn't fix the underlying lockout. Whatever mechanism in
unelevatedmode prevents the sandbox from writing inside.gitis independent of the DACL.Test 2 — Codex CLI
workspace-writeProcedure (from a host PowerShell session, with toml restored to
elevated):Result: same
error: cannot open '.git/FETCH_HEAD': Permission deniedfrom inside the sandbox.Post-test
icacls C:\acl-test\Git-Obsidian-backup\.git:Two explicit Denies + one inherited Allow against
S-1-5-21-1055846344-1817993210-866058223-1263234699. From the host's POV this SID is orphan (Translate([NTAccount])throwsIdentityNotMappedException); its prefix matches no SID issuer registered with this Windows host's LSA. Same shape as the original report (S-1-5-21-3824534274-3844480456-2110850372-3580790307from the original Codex Desktop pull): one direct Deny + one(OI)(CI)(IO)inherit-only Deny that propagates onto every newly-created child of.git.This is the fourth distinct orphan SID family observed across testing (live repo, two disposable-clone sessions, plus a workspace-root Allow side-finding). The orphan SID is per-Codex-session, not per-machine.
Test 3 — control with
--dangerously-bypass-approvals-and-sandboxResult: exit 0, fast-forwarded cleanly. Sandbox unambiguously the cause.
Updated mechanism summary
git pull --ff-onlysucceed is sandbox-bypass — exposed on the CLI as--dangerously-bypass-approvals-and-sandbox, not exposed as a config option in Codex Desktop.elevatedand CLIworkspace-write. Desktopunelevatedreproduces the lockout without writing Denies.Translatecannot see this principal because it lives in Codex's sandbox identity layer.What this implies for the original report
The original report's "Workaround" section cited the
experimental_windows_sandboxtoggle pending local test. That section is now stale — the toggle no longer exists in CLI0.128.0. There is currently no in-product way to disable the sandbox in Codex Desktop; the only working bypass is via Codex CLI's--dangerously-bypass-approvals-and-sandbox, which is not a comparable durable workflow (it disables the sandbox entirely for that exec, with all the safety implications that come with it).The writable-gitdir opt-in proposed in #14338 for the Linux bubblewrap path remains the cleanest cross-platform fix.
In the meantime, the Codex Desktop
unelevatedvariant is at least an improvement onelevatedfor the host-ACL-pollution dimension: same lockout, but no accumulating Deny ACEs that need aRemoveAccessRuleSpecificcleanup recipe to remove. Ifelevatedcannot be deprecated entirely, surfacingunelevatedas the recommended default would reduce surprise for users who don't realise their.gitdirectories are accumulating orphan-SID Deny ACEs every Codex session.Correction and closure.
After the follow-up testing, I learned that Codex Desktop has an in-app "Full Access" toggle under settings that maps to
sandbox_mode = "danger-full-access"+approval_policy = "never"— exactly the configuration that allowsgit pull --ff-onlyto succeed from inside Codex Desktop. The toggle is on by default in some installs (mine on the old laptop) and off in others (mine on the new laptop), which fully explains the asymmetry described in the original report.The original report's framing was therefore wrong on its central claim ("no in-product way to disable the sandbox in Codex Desktop"). The toggle exists. This is a settings-discovery issue on my end, not a bug in Codex.
Two sub-concerns surfaced during testing that may still be worth tracking — but better as separate, focused issues than as a reframe of this one:
error: cannot open '.git/FETCH_HEAD': Permission deniedis a generic OS error with no Codex-side hint that the sandbox is the cause or that Full Access in settings would resolve it. I spent hours chasing OneDrive ACL theories before the sandbox layer became visible. A more specific in-app error or a one-line "this command was blocked by the sandbox; see settings → Full Access" would prevent the same chase for the next user.[windows] sandbox = "elevated"writes orphan-SID Deny ACEs on.gitper session. From the host POV the SIDs don't translate (IdentityNotMappedException), so the ACEs are no-ops and don't break host Git, but they accumulate permanently in the NTFS DACL across sessions.[windows] sandbox = "unelevated"reproduces the same lockout from inside the sandbox without writing the Denies, so the pollution is specific to elevated mode rather than the sandbox layer in general. Worth noting if you're tracking sandbox-cleanup hygiene.Neither rises to the level of the original framing. Closing this; happy to file the sub-concerns separately if either is of interest, otherwise the writable-gitdir cross-platform request in #14338 covers the underlying ergonomic ask.
Apologies for the noise.
Closing per the correction comment above — original framing was wrong on its central claim. The Full Access toggle in Codex Desktop settings is the in-product disable I claimed didn't exist.
Possible Fix for Codex Git Permission Errors on Windows
I also encountered this issue while using Codex in
workspace-writemode. In my case, the repository root was writable, so Codex could still edit normal code and document files, but Git operations failed because Git needed to write metadata inside the.gitdirectory.The issue was not caused by Git itself, the repository contents, or the workspace root permissions. The problem was caused by abnormal Windows ACL entries on the
.gitdirectory.The key point is that two repositories may appear similar at the workspace level, but their actual
.gitACLs can be different. A repository root can be writable, while Git operations still fail because Git needs to create or update files such as.git\index.lockor.git\FETCH_HEAD.In the problematic repository,
.gitand.git\indexcontained additional explicit Deny ACEs. Since explicit Deny entries take precedence over Allow entries in Windows ACLs, Git could not create.git\index.lock, even though the repository root itself was writable.A possible fix is to reset the ACL of the
.gitdirectory and re-enable inheritance from the repository root. This should be run from a normal PowerShell session:```powershell id="79dxaq"
cd <path-to-your-repository>
icacls .git /reset /T /C
icacls .git /inheritance:e /T /C
I ran into a related failure from the same subsystem that may be worth cross-referencing here.
Besides the Deny ACEs on .git, the sandbox can also rewrite ownership of project directories to CodexSandboxOffline — a transient identity that stops resolving afterwards, which then blocks Codex's own attempts to repair the ACLs. Symptoms look different (SetNamedSecurityInfoW failed: 5, CreateProcessWithLogonW failed: 1056) but the trigger is the same: a sandbox operation interrupted partway through.
What reportedly fixed it permanently, from an elevated PowerShell with Codex closed:
takeown /F "<PROJECT_ROOT>" /R /D Y
icacls "<PROJECT_ROOT>" /setowner "<MACHINE>\<USER>" /T /C
icacls "<PROJECT_ROOT>" /inheritance:e /T /C
Worth checking whether you're also affected: icacls "<PROJECT_ROOT>" /T /C | Select-String "CodexSandbox"
I've been collecting the variants of this and what does/doesn't work here: https://bugswiki.com/troubleshooting/codex-couldnt-set-up-sandbox/ — the experimental_windows_sandbox = false workaround from this thread is in there too.