{"record":{"id":"5e41d4b4db4668ca","repo":"Hmbown/CodeWhale","slug":"private-sidecar-file-must-be-one-regular-filesystem-link","errorCode":null,"errorMessage":"private sidecar file {} must be one regular filesystem link","messagePattern":"private sidecar file (.+?) must be one regular filesystem link","errorType":"validation","errorClass":"InvalidData","httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":191,"sourceCode":"    }\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::OpenOptionsExt as _;\n        use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT;\n        options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);\n    }\n    let file = options.open(path)?;\n    validate_private_regular_file(&file, path)?;\n    Ok(file)\n}\n\n#[cfg(unix)]\nfn validate_private_regular_file(file: &fs::File, path: &Path) -> io::Result<()> {\n    use std::os::unix::fs::MetadataExt as _;\n\n    let metadata = file.metadata()?;\n    if !metadata.is_file() || metadata.nlink() != 1 {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"private sidecar file {} must be one regular filesystem link\",\n                path.display()\n            ),\n        ));\n    }\n    Ok(())\n}\n\n#[cfg(windows)]\nfn validate_private_regular_file(file: &fs::File, path: &Path) -> io::Result<()> {\n    use std::os::windows::fs::MetadataExt as _;\n    use std::os::windows::io::AsRawHandle as _;\n    use windows_sys::Win32::Storage::FileSystem::{\n        BY_HANDLE_FILE_INFORMATION, FILE_ATTRIBUTE_REPARSE_POINT, GetFileInformationByHandle,\n    };\n","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/session_manager.rs#L173-L209","documentation":"On Unix, session sidecar files (locks and private reads) must be plain regular files with exactly one hard link. Metadata showing nlink != 1 means someone hard-linked or manipulated the file, which could break the private-lock protocol. The manager throws InvalidData instead of using an untrusted sidecar.","triggerScenarios":"open_private_lock_file or open_private_read_file finds the target is not a regular file (directory, FIFO, device) or has more than one hard link (nlink != 1).","commonSituations":"Another process hard-linking the session lock file, sidecar paths swapped with symlinks targets or pipes via tampering or misconfigured redirection, restored backups that duplicated links.","solutions":["Remove the tampered sidecar file and let the session manager recreate it (usually ~/.local/share/codewhale session state)","Ensure no process hard-links session state files","Check for symlink/hardlink injection — this error can indicate tampering; investigate the environment","If restoring from backup, restore files, not links"],"exampleFix":"// diagnose\nls -li ~/.local/share/codewhale/sessions/   # look for link count > 1\n// fix\nrm <sidecar-with-multiple-links>            # manager recreates it on next start","handlingStrategy":"validation","validationCode":"let md = std::fs::metadata(&path)?;\nif !md.is_file() || md.nlink() != 1 { return Err(\"sidecar must be a single regular file\"); }","typeGuard":null,"tryCatchPattern":"match manager.open_session() {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains(\"filesystem link\") => recreate_sidecar(),\n    other => other,\n}","preventionTips":["Never hard-link session state files","Watch for tampering: nlink != 1 in a state dir is suspicious","Restore from file copies, not link-preserving backups"],"tags":["rust","unix","filesystem","security","hardlink"],"backgroundTag":"invalid-argument-value","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}