BigPizzaV3/CodexPlusPlus · error
Monitor lock is a reparse point
Error message
Monitor lock is a reparse point
What it means
On Windows, after confirming monitor.lock is a regular file, acquire_monitor_owner additionally rejects files whose FILE_ATTRIBUTE_REPARSE_POINT bit (0x400) is set — junctions, symlinks, OneDrive/cloud placeholders, and similar. This is an anti-symlink-attack / anti-placeholder hardening check: a reparse point could redirect the lock to a location controlled by someone else or behave unpredictably under file locking, so the library refuses to use it.
Solutions
- Check the attribute: `fsutil reparsepoint query <state_root>\monitor.lock` or `attrib <path>` and look for reparse/L attributes.
- Move the state directory to a real local NTFS path (not a junction/symlink/cloud-synced folder) and update the configuration pointing at it.
- Dehydrate/convert OneDrive placeholders to local files, or exclude the CodexPlusPlus state dir from cloud sync.
- Replace monitor.lock with a plain regular file (delete and let the library recreate it) once the state root is a plain directory.
Example fix
// before: state dir is a junction mklink /J %APPDATA%\codex-state D:\sync\codex-state // after: real local directory, not cloud-synced mkdir %APPDATA%\codex-state (copy contents, remove the junction)
Defensive patterns
Strategy: validation
Validate before calling
// Windows-only preflight
#[cfg(windows)]
fn has_reparse_point(p: &std::path::Path) -> bool {
use std::os::windows::fs::MetadataExt;
std::fs::symlink_metadata(p).map(|m| m.file_attributes() & 0x400 != 0).unwrap_or(false)
}
// refuse to use a state root (or lock path) that is a junction/symlink/cloud placeholder Type guard
#[cfg(windows)]
fn is_plain_local_file(p: &std::path::Path) -> bool {
use std::os::windows::fs::MetadataExt;
std::fs::metadata(p).map(|m| m.is_file() && m.file_attributes() & 0x400 == 0).unwrap_or(false)
} Try / catch
match start_monitor(&paths, enabled) {
Err(e) if e.to_string().contains("reparse point") => {
// relocate state dir to a real local NTFS path and retry
}
other => other?,
} Prevention
- Do not relocate the state directory with NTFS junctions or symlinks
- Exclude the CodexPlusPlus state directory from OneDrive/Dropbox placeholders and cloud sync
- Preflight the state root on Windows for the 0x400 reparse attribute before starting monitors
When it happens
Trigger: Calling start_monitor_with_contract on Windows when `<state_root>/monitor.lock` carries the reparse-point attribute — e.g. the state directory was replaced with an NTFS junction or symlink, the file is a OneDrive/Files-On-Demand placeholder, or a dev-drive/symlink setup put the lock behind a reparse point.
Common situations: State directory relocated via junction/symlink (common with Dotfiles or moving state off an SSD); OneDrive/Dropbox 'files on demand' placeholder attributes; corporate roaming profiles with folder redirection; WSL/Windows interop creating symlinks.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- File is a reparse point
- Parent directory is a reparse point
- Dream Skin path must not be a symbolic link
- Dream Skin theme path is not a safe directory
- refusing to delete script outside user script directory
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/d308e55bda1ff8e9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:743
"Backups must be outside the cache"
);
fs::create_dir_all(&paths.state_root)?;
let path = paths.state_root.join("monitor.lock");
let _guards = pin_parents(&path)?;
let mut options = OpenOptions::new();
options.read(true).write(true).create(true).truncate(false);
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
options.share_mode(0x1 | 0x2).custom_flags(0x00200000);
}
let owner = options.open(&path)?;
let meta = owner.metadata()?;
ensure!(meta.is_file(), "Unexpected monitor lock type");
#[cfg(windows)]
{
use std::os::windows::fs::MetadataExt;
ensure!(meta.file_attributes() & 0x400 == 0, "Monitor lock is a reparse point");
}
plain_path(&path)?;
owner.try_lock_exclusive().context("Another native browser monitor is active")?;
Ok(owner)
}
/// Called after Codex has been stopped, before the manager launches a replacement.
/// Never restores files itself or creates a lock for an older launcher.
pub fn wait_for_monitor_shutdown(timeout: Duration) -> Result<()> {
if !cfg!(windows) {
return Ok(());
}
let paths = BrowserPaths::current()?;
wait_for_monitor_shutdown_at(&paths, timeout)
}
fn wait_for_monitor_shutdown_at(paths: &BrowserPaths, timeout: Duration) -> Result<()> {
let path = paths.state_root.join("monitor.lock");View on GitHub (pinned to b1ed92e5e4)