Skip to content

CODEX_HOME fails to load inside a Windows AppContainer/lowbox — find_codex_home()'s canonicalize() needs \GLOBAL?? DOS-drive-letter access the sandbox denies #45871

Description

@yaakovsash1

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)

  1. Create an AppContainer profile (CreateAppContainerProfile) → package SID.
  2. 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 ~/.codex into it, set CODEX_HOME to it.
  3. Launch codex login status in the AppContainer via CreateProcess + PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES.
  4. 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

Environment

  • Windows 11 (10.0.22631), codex CLI (Rust), running inside a per-agent AppContainer (lowbox token).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingconfigIssues involving config.toml, config keys, config merging, or config updatessandboxIssues related to permissions or sandboxingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions