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

  1. Ensure nothing concurrently replaces plugin files while the app runs; retry after the update finishes.
  2. Install plugins as regular files with a single hard link (no hard-link dedup tools).
  3. 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

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


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() {
        false

View on GitHub (pinned to 73e0f67d83)