codex - 💡(How to fix) Fix [Windows] Restricted-token sandbox: --add-dir doesn't stamp SID, and os.replace() fails on stamped paths [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#22044Fetched 2026-05-11 03:20:17
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
0
Author
Timeline (top)
labeled ×4commented ×2

Codex on Windows appears to run spawned shells under a restricted Windows token. Write access therefore seems to require two checks to pass: the normal Windows ACL check and the restricted-token/restricting-SID check. A path can have ordinary user write permission and still fail if it does not carry the sandbox SID associated with the current Codex session.

The --add-dir flag appears to affect Codex's policy layer but does not stamp the sandbox SID onto the added path. In practice, this means a path declared with --add-dir is treated as allowed by Codex policy, but Windows still denies file writes because the second restricted-token SID check fails.

Separately, even on paths that are stamped with the Codex sandbox SID, Python atomic rename patterns can fail with WinError 5. Today this reproduced repeatedly with python -m py_compile, which creates a temporary .pyc.<random> file and then atomically replaces the final .pyc path. That strengthens the hypothesis that the sandbox grants write permission but interferes with delete/rename semantics required by os.replace() and similar atomic-write patterns.

Error Message

mkdtemp: PASS C:\WorkProjects<repo>.codex-tmp\tmp_leyo1ox write_text: FAIL PermissionError: [Errno 13] Permission denied sqlite: FAIL OperationalError: unable to open database file TemporaryDirectory: FAIL PermissionError: [WinError 5] Access is denied cleanup: FAIL PermissionError: [WinError 5] Access is denied

Root Cause

The --add-dir flag appears to affect Codex's policy layer but does not stamp the sandbox SID onto the added path. In practice, this means a path declared with --add-dir is treated as allowed by Codex policy, but Windows still denies file writes because the second restricted-token SID check fails.

Fix Action

Fix / Workaround

Operator-side workarounds attempted

Code Example

Privilege Name                Description                          State
============================= ==================================== =======
SeChangeNotifyPrivilege       Bypass traverse checking             Enabled

---

workspace = S-1-5-21-XXXX-XXXX-XXXX-XXXX
workspace_by_cwd:
  c:/users/<user> = S-1-5-21-XXXX-XXXX-XXXX-XXXX
  c:/workprojects = S-1-5-21-XXXX-XXXX-XXXX-XXXX
  c:/workprojects/<repo> = S-1-5-21-XXXX-XXXX-XXXX-XXXX

---

C:\WorkProjects\<repo>
  S-1-5-21-XXXX-XXXX-XXXX-XXXX:(I)(OI)(CI)(RX,W)
  ...standard inherited ACL entries...

---

C:\tmp
  BUILTIN\Administrators:(I)(F)
  NT AUTHORITY\SYSTEM:(I)(F)
  NT AUTHORITY\Authenticated Users:(I)(M)
  ...no S-1-5-21-XXXX-XXXX-XXXX-XXXX sandbox SID...

---

mkdtemp: PASS C:\WorkProjects\<repo>\.codex-tmp\tmp_leyo1ox
write_text: FAIL PermissionError: [Errno 13] Permission denied
sqlite: FAIL OperationalError: unable to open database file
TemporaryDirectory: FAIL PermissionError: [WinError 5] Access is denied
cleanup: FAIL PermissionError: [WinError 5] Access is denied

---

[WinError 5] Access is denied: 'pipeline\\__pycache__\\scheduler.cpython-313.pyc.2366002414384' -> 'pipeline\\__pycache__\\scheduler.cpython-313.pyc'

---

icacls C:\WorkProjects\<repo>
icacls C:\tmp

---

codex --add-dir C:\tmp

---

python -m py_compile pipeline/scheduler.py

---

[WinError 5] Access is denied: 'pipeline\\__pycache__\\scheduler.cpython-313.pyc.2366002414384' -> 'pipeline\\__pycache__\\scheduler.cpython-313.pyc'
RAW_BUFFERClick to expand / collapse

What version of Codex CLI is running?

codex-cli 0.130.0

What subscription do you have?

ChatGPT Pro

Which model were you using?

gpt-5.5 default

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What terminal emulator and version are you using (if applicable)?

Windows PowerShell 5.1 (host: ConsoleHost)

What issue are you seeing?

Summary

Codex on Windows appears to run spawned shells under a restricted Windows token. Write access therefore seems to require two checks to pass: the normal Windows ACL check and the restricted-token/restricting-SID check. A path can have ordinary user write permission and still fail if it does not carry the sandbox SID associated with the current Codex session.

The --add-dir flag appears to affect Codex's policy layer but does not stamp the sandbox SID onto the added path. In practice, this means a path declared with --add-dir is treated as allowed by Codex policy, but Windows still denies file writes because the second restricted-token SID check fails.

Separately, even on paths that are stamped with the Codex sandbox SID, Python atomic rename patterns can fail with WinError 5. Today this reproduced repeatedly with python -m py_compile, which creates a temporary .pyc.<random> file and then atomically replaces the final .pyc path. That strengthens the hypothesis that the sandbox grants write permission but interferes with delete/rename semantics required by os.replace() and similar atomic-write patterns.

Evidence

Restricted token

Inside a Codex-spawned PowerShell, whoami /priv showed only SeChangeNotifyPrivilege. A normal operator PowerShell on the same host has more privileges. Admin group memberships appeared as Group used for deny only, which is consistent with a Windows restricted token.

Privilege Name                Description                          State
============================= ==================================== =======
SeChangeNotifyPrivilege       Bypass traverse checking             Enabled

Sandbox SID mapping

Codex maintains a per-path sandbox SID mapping at C:\Users\<user>\.codex\cap_sid. Actual SID values are redacted here.

workspace = S-1-5-21-XXXX-XXXX-XXXX-XXXX
workspace_by_cwd:
  c:/users/<user> = S-1-5-21-XXXX-XXXX-XXXX-XXXX
  c:/workprojects = S-1-5-21-XXXX-XXXX-XXXX-XXXX
  c:/workprojects/<repo> = S-1-5-21-XXXX-XXXX-XXXX-XXXX

These SIDs are stamped onto listed paths' ACLs as sandbox allow ACEs.

ACL comparison

Stamped paths include the sandbox SID allow ACE:

C:\WorkProjects\<repo>
  S-1-5-21-XXXX-XXXX-XXXX-XXXX:(I)(OI)(CI)(RX,W)
  ...standard inherited ACL entries...

An unstamped path such as C:\tmp has ordinary ACL entries but no Codex sandbox SID:

C:\tmp
  BUILTIN\Administrators:(I)(F)
  NT AUTHORITY\SYSTEM:(I)(F)
  NT AUTHORITY\Authenticated Users:(I)(M)
  ...no S-1-5-21-XXXX-XXXX-XXXX-XXXX sandbox SID...

That explains the observed behavior: the standard ACL check passes, but the restricted-token SID check fails.

Failed experiments

  • Experiment 1: --add-dir alone. mkdtemp could create a directory, but file writes, SQLite creation, TemporaryDirectory lifecycle, and cleanup failed with permission/access-denied errors.
  • Experiment 2: --add-dir plus --sandbox workspace-write. Same failure pattern as Experiment 1. Broadening sandbox mode did not make file-content writes succeed.
  • Experiment 3: --ignore-user-config. Inconclusive because the flag is not available on the top-level interactive codex command, and the attempted codex exec path failed at PowerShell prompt-quoting before reaching Codex.
  • Experiment 4: reset ~/.codex/cache and ~/.codex/tmp. Same failure pattern. Resetting cache/tmp state did not affect ACL/SID behavior.

Representative failure shape:

mkdtemp: PASS C:\WorkProjects\<repo>\.codex-tmp\tmp_leyo1ox
write_text: FAIL PermissionError: [Errno 13] Permission denied
sqlite: FAIL OperationalError: unable to open database file
TemporaryDirectory: FAIL PermissionError: [WinError 5] Access is denied
cleanup: FAIL PermissionError: [WinError 5] Access is denied

Specific failure patterns

Original failure pattern: tempfile.TemporaryDirectory(dir=<repo subpath>) could create the scratch directory, but operations inside the directory failed or cleanup failed. This affects tests and audit utilities that expect temp directories under a declared writable root to behave normally.

Original atomic-write pattern: application tests using tempfile.mkstemp(dir=STATE_FILE.parent) followed by os.replace() failed inside test-created scratch directories under a stamped repo path. The repo path itself is stamped with the Codex sandbox SID, but the atomic replace still failed.

New reproduction from 2026-05-10: python -m py_compile <file>.py repeatedly failed to atomically replace __pycache__\<module>.cpython-313.pyc.<random> with the final __pycache__\<module>.cpython-313.pyc name. The failure was [WinError 5] Access is denied on the rename/replace step. This happened on paths inside cap_sid's workspace_by_cwd entries, meaning the path was stamped but the replace still failed.

Example:

[WinError 5] Access is denied: 'pipeline\\__pycache__\\scheduler.cpython-313.pyc.2366002414384' -> 'pipeline\\__pycache__\\scheduler.cpython-313.pyc'

The py_compile reproduction is significant because it occurs on a stamped path. It confirms the os.replace() nuance is a real second bug, not just an artifact of attempting writes to unstamped paths.

Full-suite pytest is therefore unreliable inside Codex on this Windows host because test frameworks and Python tooling commonly use temp directories, cache writes, and atomic replace patterns.

Recommended fixes

  1. Stamp the sandbox SID onto --add-dir target paths the same way workspace paths are stamped, OR
  2. Document that --add-dir is policy-only and add a separate flag (e.g., --writable-root) that does the ACL stamping, OR
  3. Adjust the restricted token's restricting SIDs to allow paths declared via --add-dir without requiring per-path SID stamping

For the os.replace() nuance: the sandbox setup must grant delete/rename rights on parent directories of stamped paths, not just modify rights, so atomic-rename patterns work as expected.

Operator-side workarounds attempted

  • PYTHONDONTWRITEBYTECODE=1: works for general Python execution but does not help py_compile, whose purpose is bytecode generation.
  • ast.parse() as a syntax-only validation substitute: works for narrow syntax-check use cases but is not a general substitute for py_compile or test execution.
  • Running validation from a normal operator shell outside Codex: works but breaks the agent loop and requires manual intervention.
  • Pre-creating or fixing __pycache__ ACLs from an operator shell: brittle because Codex sandbox SIDs can be session/install specific and may rotate between sessions.

None of these are durable fixes. They all require working around Codex rather than working within Codex.

Impact

  • Audit and test workflows are blocked or partially blocked inside Codex on Windows.
  • Full-suite pytest validation has to be run from an operator shell, breaking the agent loop.
  • py_compile and similar tools can partially write temporary bytecode/cache files and then fail on the atomic rename.
  • We've had to encode a local rule for roughly a week: do not gate on Codex test counts because the sandbox can make test/compile failures environmental instead of code-related.

What steps can reproduce the bug?

  1. Start a fresh Codex session on Windows 11.
  2. Run whoami /priv from the Codex shell and observe the restricted-token shape (only SeChangeNotifyPrivilege).
  3. Inspect the Codex sandbox SID mapping at ~/.codex/cap_sid to see which paths are stamped.
  4. Compare ACLs for a stamped workspace path and an unstamped path:
icacls C:\WorkProjects\<repo>
icacls C:\tmp

The stamped path shows an S-1-5-21-... sandbox SID allow ACE; the unstamped path does not.

  1. Try writing to an unstamped path such as C:\tmp from inside the Codex shell. It fails, which is expected under the restricted-token model if the path lacks the sandbox SID.

  2. Start Codex with --add-dir pointing at the previously unstamped path:

codex --add-dir C:\tmp

Then try the write again from inside the Codex shell. It still fails because --add-dir appears to update policy but does not stamp the SID. This is bug 1.

  1. On a stamped path inside the workspace, run:
python -m py_compile pipeline/scheduler.py
  1. Observe failure on the atomic .pyc.<random> to .pyc replace step, even though the path is stamped:
[WinError 5] Access is denied: 'pipeline\\__pycache__\\scheduler.cpython-313.pyc.2366002414384' -> 'pipeline\\__pycache__\\scheduler.cpython-313.pyc'

This is bug 2 — the sandbox grants write permission but interferes with the rename/delete semantics required by os.replace().

What is the expected behavior?

Two expected behaviors, one per bug:

For --add-dir (bug 1): When a user declares a path with --add-dir, file writes inside that path should succeed. Either the flag should stamp the sandbox SID onto the declared path's ACL the same way workspace paths are stamped, or the restricted token's restricting SID set should be adjusted so that --add-dir-declared paths don't require per-path SID stamping.

If --add-dir is intended to be policy-only (no ACL changes), that should be documented, and a separate flag (e.g., --writable-root) should exist that does the ACL stamping for users who actually need write access to additional paths.

For os.replace() on stamped paths (bug 2): Atomic file rename and replace operations should succeed inside paths that the sandbox has stamped with its SID. Currently, write permission is granted but rename/delete semantics fail. The sandbox setup should grant delete/rename rights on parent directories of stamped paths, not just modify rights, so that standard Python tooling — tempfile, os.replace(), py_compile, pytest, and anything else that uses atomic-write patterns — works as expected.

Net effect: an operator should be able to run python -m py_compile, pytest, and audit utilities inside the Codex shell on Windows without environmental failures unrelated to the code under test.

Additional information

  • 1Password CLI is in use on this host for unrelated workflow reasons. It is not involved in the failure.
  • All four failed experiments (Sec 9 of internal audit) were re-run in fresh Codex sessions to rule out stale state.
  • The os.replace() nuance was originally hypothesized in the audit and then confirmed today on a fresh, stamped path via python -m py_compile. The two bugs are related (both stem from the restricted-token sandbox model on Windows) but are distinct fixes.
  • Happy to share full icacls output, the unredacted cap_sid (privately, not in this public issue), or run any specific reproduction the engineering team wants.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix [Windows] Restricted-token sandbox: --add-dir doesn't stamp SID, and os.replace() fails on stamped paths [2 comments, 2 participants]