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
- 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).
- Point the feature at a real (non-reparse) path, e.g. ensure %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node lives under physical directories.
- 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.
- 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
- Install the runtime under physical directories, never behind mklink junctions or symlinks
- Exclude %LOCALAPPDATA%\OpenAI from OneDrive/Files-On-Demand redirection
- Audit the path with `dir /AL /S` or fsutil reparsepoint query before automating operations
- Keep relocation tools (backup movers, drive migrators) from junctioning user-profile directories
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
- File is a reparse point
- Dream Skin path must not be a symbolic link
- Monitor lock is a reparse point
- Reparse observation file
- 拒绝删除文件系统根目录:
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)