{"record":{"id":"f46fadc474e41a2d","repo":"Hmbown/CodeWhale","slug":"private-sidecar-file-path-must-have-exactly-one-filesystem","errorCode":null,"errorMessage":"private sidecar file {path} must have exactly one filesystem link","messagePattern":"private sidecar file (.+?) must have exactly one filesystem link","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":226,"sourceCode":"    };\n\n    let metadata = file.metadata()?;\n    if !metadata.is_file() || metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"private sidecar file {} must be a non-reparse regular file\",\n                path.display()\n            ),\n        ));\n    }\n    let mut info = BY_HANDLE_FILE_INFORMATION::default();\n    // SAFETY: `file` keeps the handle valid and `info` is writable for the call.\n    if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &mut info) } == 0 {\n        return Err(io::Error::last_os_error());\n    }\n    if info.nNumberOfLinks != 1 {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"private sidecar file {} must have exactly one filesystem link\",\n                path.display()\n            ),\n        ));\n    }\n    Ok(())\n}\n\n#[cfg(all(not(unix), not(windows)))]\nfn validate_private_regular_file(file: &fs::File, path: &Path) -> io::Result<()> {\n    if !file.metadata()?.is_file() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"private sidecar file {} must be regular\", path.display()),\n        ));\n    }","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L208-L244","documentation":"On Windows, after confirming the sidecar is a regular non-reparse file, session_manager checks via GetFileInformationByHandle that nNumberOfLinks is exactly 1. More than one link means the file is hard-linked elsewhere, so another path could observe or tamper with the private file; such files are rejected as InvalidData.","triggerScenarios":"Calling open_private_lock_file or open_private_read_file on Windows when the sidecar file has 2+ hard links (nNumberOfLinks != 1).","commonSituations":"A hardlink-creating backup tool (e.g. rsync-like dedupe, Time-Machine-style snapshots on NTFS) linked the session file; the user hardlinked files manually; a restore tool reconstructed the tree with hardlinks.","solutions":["Find the extra links (e.g. `fsutil hardlink list <path>`) and delete them, leaving one link.","Restore the file from a copy that is not hardlinked.","Disable hardlink-based dedupe/backup for the sessions directory."],"exampleFix":"// before: file duplicated via hardlink by backup tool\nfsutil hardlink list session.lock  // shows 2 links\n// after: keep one link\ncopy session.lock session.lock.new && move /y session.lock.new session.lock","handlingStrategy":"validation","validationCode":"// Windows: reject hardlinked files before opening\nfn has_single_link(p: &Path) -> bool {\n    std::fs::metadata(p).map(|m| m.number_of_links() == Some(1)).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match open_private_read_file(path) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => eprintln!(\"hardlink detected, restore a single-link copy\"),\n    other => other?,\n}","preventionTips":["Disable hardlink dedupe in backup tools for the sessions dir.","Restore session state from real copies, not hardlinked trees.","Periodically run `fsutil hardlink list` on session files if backups run locally."],"tags":["windows","filesystem","hardlink"],"backgroundTag":"incompatible-source-type","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}