Hmbown/CodeWhale · error
plugin identity probe found a reparse point, hard link, or…
Error message
plugin identity probe found a reparse point, hard link, or unexpected object
What it means
A final identity probe re-checks the opened plugin file or directory: for files it requires is_file() plus a single hard link; for directories just is_dir(). Either kind must not carry the reparse-point attribute. This is the last line of defense against TOCTOU swaps between initial open and use.
Solutions
- Ensure nothing concurrently replaces plugin files while the app runs; retry after the update finishes.
- Install plugins as regular files with a single hard link (no hard-link dedup tools).
- Fix the expected kind: point the file entry at a file and the directory entry at a directory.
Example fix
// before: updater hard-links new plugin over old
fs.link("/cache/plugin-v2.dll", "/plugins/plugin.dll")?;
// after: copy so links stays 1
fs.copy("/cache/plugin-v2.dll", "/plugins/plugin.dll")?; Defensive patterns
Strategy: validation
Validate before calling
let md = std::fs::metadata(&path)?;
let kind_ok = if expect_dir { md.is_dir() } else { md.is_file() };
assert!(kind_ok && !md.file_type().is_symlink(), "plugin path swapped or linked mid-flight"); Type guard
fn probe_ok(md: &std::fs::Metadata, expect_dir: bool) -> bool { if expect_dir { md.is_dir() } else { md.is_file() } && !md.file_type().is_symlink() } Try / catch
match probe(&path) { Err(e) if e.to_string().contains("identity probe") => retry_after_plugin_update(), Err(e) => return Err(e), Ok(f) => use(f), } Prevention
- Pause plugin auto-updaters while the app is running to avoid TOCTOU swaps
- Deploy plugin updates by copy/replace, never by hard links
- Verify expected file vs directory kind in the plugin manifest
When it happens
Trigger: Calling the identity probe (crates/tui/src/plugins/manifest.rs:1641) when the file was replaced by a symlink/hard link after the earlier check, links != 1 on a file, wrong object kind (directory where file expected or vice versa), or any reparse-point attribute.
Common situations: Concurrent replacement of a plugin file during update (TOCTOU); updater tooling swapping plugin files via rename+link; misconfigured path pointing at the wrong object type.
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 directory is a reparse point or non-directory
- plugin file is a reparse point, hard link, or non-regular…
- 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/6c73a7ef5ab1fad8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/plugins/manifest.rs:1641
| if expect_directory {
FILE_FLAG_BACKUP_SEMANTICS
} else {
0
};
let file = fs::OpenOptions::new()
.access_mode(0)
.share_mode(FILE_SHARE_READ_WRITE_DELETE)
.custom_flags(flags)
.open(path)?;
let metadata = file.metadata()?;
let identity = windows_file_identity(&file)?;
let expected_kind = if expect_directory {
metadata.is_dir()
} else {
metadata.is_file() && identity.links == 1
};
if !expected_kind || identity.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"plugin identity probe found a reparse point, hard link, or unexpected object",
));
}
Ok(file)
}
#[cfg(windows)]
fn ensure_windows_path_still_opened(path: &Path, opened: &fs::File) -> Result<(), String> {
let after = fs::symlink_metadata(path)
.map_err(|e| format!("failed to re-inspect plugin path after handle open: {e}"))?;
if metadata_is_link_or_reparse(&after) {
return Err("plugin path changed into a reparse point during validation".to_string());
}
let expect_directory = if after.is_dir() {
true
} else if after.is_file() {
falseView on GitHub (pinned to 73e0f67d83)