BigPizzaV3/CodexPlusPlus · error

Parent directory is a reparse point

Error message

Parent directory is a reparse point

What it means

pin_parents opens every ancestor directory of the target path root-first with OPEN_REPARSE_POINT and FILE_SHARE flags that deny DELETE, to pin them so no ancestor can be swapped for a junction/symlink mid-operation (anti-TOCTOU hardening for browser-service patching). It then verifies each opened parent is a real directory (FILE_ATTRIBUTE_REPARSE_POINT, 0x400, clear). If any ancestor resolves to a reparse point — a junction, symlink, mount point, or OneDrive placeholder — the write/read is aborted rather than following the link.

Solutions

  1. Remove the junction/symlink: move the real directory to the intended location and use it directly instead of linking (rmdir removes a junction without touching the target).
  2. Point the feature at a real (non-reparse) path, e.g. ensure %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node lives under physical directories.
  3. If OneDrive redirection is the cause, exclude the path from Files-On-Demand / keep-it-local so the directory is not a cloud-placeholder reparse point.
  4. Re-run the operation after the path is a plain directory; this check is a hard security boundary and cannot be bypassed via configuration.

Example fix

// before: runtime relocated to D: via a junction
mklink /J %LOCALAPPDATA%\OpenAI D:\OpenAI
// after: keep the real directory on the original volume or reconfigure the runtime root
robocopy /MOVE D:\OpenAI %LOCALAPPDATA%\OpenAI /E
Defensive patterns

Strategy: validation

Validate before calling

fn assert_plain_parents(path: &Path) -> std::io::Result<()> {
    for ancestor in path.ancestors().skip(1) {
        let meta = std::fs::symlink_metadata(ancestor)?;
        #[cfg(windows)]
        {
            use std::os::windows::fs::MetadataExt;
            if meta.file_attributes() & 0x400 != 0 {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidInput,
                    format!("{} is a reparse point", ancestor.display()),
                ));
            }
        }
        if meta.file_type().is_symlink() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!("{} is a symlink", ancestor.display()),
            ));
        }
    }
    Ok(())
}

Type guard

fn is_plain_dir(p: &Path) -> bool {
    match std::fs::symlink_metadata(p) {
        Ok(m) => m.is_dir() && !m.file_type().is_symlink(),
        Err(_) => false,
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("reparse point") => {
        eprintln!("Path chain contains a junction/symlink: resolve it manually: {e}");
    }
    Err(e) => return Err(e.into()),
    Ok(v) => use_value(v),
}

Prevention

When it happens

Trigger: Calling any API that writes or reads through read_regular/write_new/atomic_write_with_modified (prepare, restore_all, reconcile_contract) when an ancestor directory of the target path is a Windows junction, symlink, mount point, or other reparse point; e.g. the runtime root sits under a junctioned directory or LOCALAPPDATA was relocated via a junction.

Common situations: Users relocate %LOCALAPPDATA%\OpenAI to another drive with a junction; developer setups symlink parts of the tree; OneDrive/Files-On-Demand folder redirection makes directories cloud placeholders (reparse points); antivirus or backup tools create mount points under user profile directories.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/bf0bc61009068a85. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/native_browser.rs:156

fn pin_parents(path: &Path) -> Result<Vec<File>> {
    plain_path(path)?;
    let mut guards = Vec::new();
    #[cfg(windows)]
    {
        use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
        let mut parents: Vec<_> = path.ancestors().skip(1).collect();
        parents.reverse();
        for parent in parents {
            if !parent.exists() {
                break;
            }
            let guard = OpenOptions::new()
                .read(true)
                .share_mode(0x1 | 0x2) // FILE_SHARE_READ | FILE_SHARE_WRITE, never DELETE.
                .custom_flags(0x02000000 | 0x00200000) // BACKUP_SEMANTICS | OPEN_REPARSE_POINT.
                .open(parent)?;
            let meta = guard.metadata()?;
            ensure!(
                meta.is_dir() && meta.file_attributes() & 0x400 == 0,
                "Parent directory is a reparse point"
            );
            guards.push(guard);
        }
    }
    Ok(guards)
}

fn read_regular(path: &Path, limit: u64) -> Result<Vec<u8>> {
    let _guards = pin_parents(path)?;
    let mut options = OpenOptions::new();
    options.read(true);
    #[cfg(windows)]
    {
        use std::os::windows::fs::OpenOptionsExt;
        options.share_mode(0x1).custom_flags(0x00200000);
    }

View on GitHub (pinned to b1ed92e5e4)