{"record":{"id":"bf0bc61009068a85","repo":"BigPizzaV3/CodexPlusPlus","slug":"parent-directory-is-a-reparse-point","errorCode":null,"errorMessage":"Parent directory is a reparse point","messagePattern":"Parent directory is a reparse point","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/native_browser.rs","lineNumber":156,"sourceCode":"fn pin_parents(path: &Path) -> Result<Vec<File>> {\n    plain_path(path)?;\n    let mut guards = Vec::new();\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::{MetadataExt, OpenOptionsExt};\n        let mut parents: Vec<_> = path.ancestors().skip(1).collect();\n        parents.reverse();\n        for parent in parents {\n            if !parent.exists() {\n                break;\n            }\n            let guard = OpenOptions::new()\n                .read(true)\n                .share_mode(0x1 | 0x2) // FILE_SHARE_READ | FILE_SHARE_WRITE, never DELETE.\n                .custom_flags(0x02000000 | 0x00200000) // BACKUP_SEMANTICS | OPEN_REPARSE_POINT.\n                .open(parent)?;\n            let meta = guard.metadata()?;\n            ensure!(\n                meta.is_dir() && meta.file_attributes() & 0x400 == 0,\n                \"Parent directory is a reparse point\"\n            );\n            guards.push(guard);\n        }\n    }\n    Ok(guards)\n}\n\nfn read_regular(path: &Path, limit: u64) -> Result<Vec<u8>> {\n    let _guards = pin_parents(path)?;\n    let mut options = OpenOptions::new();\n    options.read(true);\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::OpenOptionsExt;\n        options.share_mode(0x1).custom_flags(0x00200000);\n    }","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/native_browser.rs#L138-L174","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: runtime relocated to D: via a junction\nmklink /J %LOCALAPPDATA%\\OpenAI D:\\OpenAI\n// after: keep the real directory on the original volume or reconfigure the runtime root\nrobocopy /MOVE D:\\OpenAI %LOCALAPPDATA%\\OpenAI /E","handlingStrategy":"validation","validationCode":"fn assert_plain_parents(path: &Path) -> std::io::Result<()> {\n    for ancestor in path.ancestors().skip(1) {\n        let meta = std::fs::symlink_metadata(ancestor)?;\n        #[cfg(windows)]\n        {\n            use std::os::windows::fs::MetadataExt;\n            if meta.file_attributes() & 0x400 != 0 {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidInput,\n                    format!(\"{} is a reparse point\", ancestor.display()),\n                ));\n            }\n        }\n        if meta.file_type().is_symlink() {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                format!(\"{} is a symlink\", ancestor.display()),\n            ));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_plain_dir(p: &Path) -> bool {\n    match std::fs::symlink_metadata(p) {\n        Ok(m) => m.is_dir() && !m.file_type().is_symlink(),\n        Err(_) => false,\n    }\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"reparse point\") => {\n        eprintln!(\"Path chain contains a junction/symlink: resolve it manually: {e}\");\n    }\n    Err(e) => return Err(e.into()),\n    Ok(v) => use_value(v),\n}","preventionTips":["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"],"tags":["windows","filesystem","symlink","security"],"backgroundTag":"incompatible-source-type","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}