`CODEX_HOME` fails to load inside a Windows AppContainer/lowbox — `find_codex_home()`'s `canonicalize()` needs `\GLOBAL??` DOS-drive-letter access the sandbox denies
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
When CODEX_HOME is set and codex runs inside a Windows AppContainer (lowbox token), startup fails with:
WARNING: proceeding, even though we could not create PATH aliases: failed to canonicalize CODEX_HOME "<dir>": Access is denied. (os error 5)
Error loading configuration: failed to canonicalize CODEX_HOME "<dir>": Access is denied. (os error 5)
even though the process can otherwise read and write that same directory. The cause is find_codex_home() calling
PathBuf::from(val).canonicalize() on CODEX_HOME. std::fs::canonicalize on Windows resolves the final path via
GetFinalPathNameByHandleW(..., VOLUME_NAME_DOS), and an AppContainer token cannot resolve the C:\ DOS drive
letter because that requires access to \GLOBAL??\C: (the DosDevices symbolic link in the object namespace), which
lowbox tokens are denied. It is not a filesystem-ACL problem — granting the package/capability SID or ALL APPLICATION PACKAGES on the directory (or the drive root) does not help.
This matters because per-agent isolation on Windows (e.g. running multiple codex instances each confined to its own
CODEX_HOME inside its own AppContainer — the same AppContainer approach OpenAI's own Codex Windows sandbox uses) is
exactly the case that must set CODEX_HOME, and it's the only find_codex_home() branch that canonicalizes.
Root cause (isolated empirically)
Inside an AppContainer, we decomposed what canonicalize does with a native probe on the process's own
(read/writable) directory:
| Step | API | Result inside AppContainer |
|---|---|---|
| Open the dir | CreateFileW(access=0, FILE_FLAG_BACKUP_SEMANTICS) |
OK |
| Resolve NT path | GetFinalPathNameByHandleW(VOLUME_NAME_NT) |
OK (\Device\HarddiskVolumeN\…) |
| Resolve DOS path | GetFinalPathNameByHandleW(VOLUME_NAME_DOS) |
FAIL — Access denied (needs \GLOBAL??\C:) |
So the dir open succeeds and NT-path resolution succeeds; only the DOS drive-letter resolution (which
std::fs::canonicalize uses) is denied. Plain std::fs::read/write on the same dir work — they don't do the
\GLOBAL?? resolution, so only canonicalize trips it.
The offending call is codex-rs/core/src/config.rs, find_codex_home():
pub fn find_codex_home() -> std::io::Result<PathBuf> {
if let Ok(val) = std::env::var("CODEX_HOME") {
if !val.is_empty() {
return PathBuf::from(val).canonicalize(); // <-- fails inside an AppContainer
}
}
// ... (the unset branch does not canonicalize)
}
Proposed fix (one-liner, no behavior change on normal setups)
Don't hard-fail on canonicalize; fall back to a pure-string absolutization that opens no handle and does no
\GLOBAL?? resolution. This is the well-established try_canonicalize pattern (used by cargo/rustc for exactly this
class of Windows/sandbox canonicalize unreliability — see rust-lang/rust#79449, cargo#11866, std::path::absolute
rust-lang/rust#91673):
if let Ok(val) = std::env::var("CODEX_HOME") {
if !val.is_empty() {
let p = PathBuf::from(val);
// canonicalize() opens a handle + resolves the DOS drive letter via \GLOBAL??, which a
// Windows AppContainer/lowbox token is denied. Fall back to a pure-string absolute path
// (GetFullPathNameW on Windows) so CODEX_HOME works inside a sandbox.
return p.canonicalize().or_else(|_| std::path::absolute(&p));
}
}
std::path::absolute (stable since 1.79) is GetFullPathNameW-based on Windows — no directory handle, no object-
namespace lookup — so it succeeds in the AppContainer while remaining a no-op-equivalent for already-absolute paths on
normal setups. (If std::path::absolute isn't desired, dunce::canonicalize or a manual GetFullPathNameW are
equivalents; the key point is: don't require a handle-opening canonicalize on CODEX_HOME.)
Repro (Windows 11)
- Create an AppContainer profile (
CreateAppContainerProfile) → package SID. - Make a dir (e.g.
C:\codex-home-test) and grant that package SID Full (so the process can read/write it), seed a
normal~/.codexinto it, setCODEX_HOMEto it. - Launch
codex login statusin the AppContainer viaCreateProcess+PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES. - Observe the
failed to canonicalize CODEX_HOME … Access is denied (os error 5)error, despite the process being
able to read/write the dir.GetFinalPathNameByHandleW(VOLUME_NAME_NT)on the same handle succeeds;VOLUME_NAME_DOS
fails.
Related
- Ties to the Codex Windows sandbox ACL/Error-5 issues (e.g. #13378 — the sandbox setup failing to establish drive-root
access). This is the path-resolution counterpart: even with directory ACLs correct,canonicalizeonCODEX_HOME
fails on the\GLOBAL??DOS-drive-letter step.
Environment
- Windows 11 (10.0.22631), codex CLI (Rust), running inside a per-agent AppContainer (lowbox token).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in codex-rs/core/src/config.rs at find_codex_home() and inspect the CODEX_HOME branch that calls canonicalize(). Reproduce or verify behavior with CODEX_HOME inside a Windows AppContainer, then confirm the fallback permits startup without DOS-drive-letter resolution while preserving normal behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100