Hmbown/CodeWhale · error
plugin directory is a reparse point or non-directory
Error message
plugin directory is a reparse point or non-directory
What it means
The plugin loader opens a plugin directory on Windows with BACKUP_SEMANTICS | OPEN_REPARSE_POINT flags and verifies it is truly a directory and not a reparse point (symlink/junction/mount point). It throws InvalidData when the directory is a link or not a directory, to prevent plugin discovery from following links outside the plugin root.
Solutions
- Replace the junction/symlinked plugin directory with a real directory containing the plugin files.
- Move the actual plugin directory to the expected location instead of linking it.
- Verify the configured plugin directory path refers to a real directory, not a file.
Example fix
// before (cmd.exe) mklink /J plugins\my-plugin D:\shared\my-plugin // after robocopy D:\shared\my-plugin plugins\my-plugin /E // real directory
Defensive patterns
Strategy: validation
Validate before calling
let md = std::fs::metadata(&dir)?; assert!(md.is_dir(), "plugin directory must be a real directory"); assert!(md.file_type().is_symlink() == false, "plugin directory must not be a symlink/junction");
Type guard
fn is_plain_dir(md: &std::fs::Metadata) -> bool { md.is_dir() && !md.file_type().is_symlink() } Try / catch
if let Err(e) = load_plugins(&dir) { if e.kind() == std::io::ErrorKind::InvalidData { eprintln!("replace {} with a real directory", dir.display()); } } Prevention
- Do not use mklink /J for the plugins folder; use a real directory on the target drive
- Exclude the plugins directory from tools that create reparse placeholders (OneDrive Files On-Demand)
- Verify path kind with fs::metadata before configuring it as the plugin dir
When it happens
Trigger: Plugin directory path (crates/tui/src/plugins/manifest.rs:1599) is a junction, directory symlink, or mount point (FILE_ATTRIBUTE_REPARSE_POINT), or metadata().is_dir() is false (file passed as directory).
Common situations: Plugins directory managed as a junction to another drive; directory symlink from a package manager or dotfile manager; passing a file path where the plugin directory is expected.
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
- plugin file is a reparse point, hard link, or non-regular…
- plugin identity probe found a reparse point, hard link, or…
- Automation lock must not be a reparse point
- Codewhale-owned credential file must be singly linked
- Codewhale-owned xAI OAuth path has the wrong filesystem type
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a88232acd2204ecd.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/plugins/manifest.rs:1599
#[cfg(all(not(unix), not(windows)))]
pub(crate) fn open_bundle_file(path: &Path) -> std::io::Result<fs::File> {
fs::File::open(path)
}
#[cfg(windows)]
fn open_bundle_directory(path: &Path) -> std::io::Result<fs::File> {
use std::os::windows::fs::OpenOptionsExt as _;
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
let file = fs::OpenOptions::new()
.read(true)
.share_mode(0x0000_0001)
.custom_flags(0x0220_0000) // BACKUP_SEMANTICS | OPEN_REPARSE_POINT
.open(path)?;
let metadata = file.metadata()?;
let identity = windows_file_identity(&file)?;
if !metadata.is_dir() || identity.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"plugin directory is a reparse point or non-directory",
));
}
Ok(file)
}
/// Reopen a reviewed path only to compare its current handle identity with a
/// retained authority handle. Desired access is zero and sharing is permissive
/// because the retained handle remains responsible for denying writes and
/// replacement throughout the comparison.
#[cfg(windows)]
pub(crate) fn open_bundle_identity_probe(
path: &Path,
expect_directory: bool,
) -> std::io::Result<fs::File> {
use std::os::windows::fs::OpenOptionsExt as _;
View on GitHub (pinned to 73e0f67d83)