{"record":{"id":"2e2b3fd3d8a9cfc6","repo":"Hmbown/CodeWhale","slug":"late-usage-store-must-be-a-real-directory","errorCode":null,"errorMessage":"late usage store must be a real directory","messagePattern":"late usage store must be a real directory","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":1412,"sourceCode":"        &self.sessions_dir\n    }\n\n    fn late_usage_paths(&self, session_id: &str) -> io::Result<(PathBuf, PathBuf)> {\n        let session_id = self.validated_session_id(session_id)?;\n        let dir = self.sessions_dir.join(LATE_USAGE_DIR);\n        match fs::symlink_metadata(&dir) {\n            Ok(metadata) => {\n                #[cfg(windows)]\n                let linked = {\n                    use std::os::windows::fs::MetadataExt as _;\n                    metadata.file_attributes()\n                        & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT\n                        != 0\n                };\n                #[cfg(not(windows))]\n                let linked = metadata.file_type().is_symlink();\n                if linked || !metadata.is_dir() {\n                    return Err(io::Error::new(\n                        io::ErrorKind::InvalidData,\n                        \"late usage store must be a real directory\",\n                    ));\n                }\n            }\n            Err(error) if error.kind() == io::ErrorKind::NotFound => {}\n            Err(error) => return Err(error),\n        }\n        Ok((\n            dir.join(format!(\"{session_id}.json\")),\n            dir.join(format!(\"{session_id}.lock\")),\n        ))\n    }\n\n    /// Only mutations create accounting storage. Snapshot/list reads must work\n    /// for a healthy transcript even when no sidecar has ever been written.\n    fn ensure_late_usage_paths(&self, session_id: &str) -> io::Result<(PathBuf, PathBuf)> {\n        self.late_usage_paths(session_id)?;","sourceCodeStart":1394,"sourceCodeEnd":1430,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L1394-L1430","documentation":"The late usage store path checks its directory using metadata with a Windows reparse-point test and a Unix is_symlink test, and errors with InvalidData 'late usage store must be a real directory' if the path is linked or not a directory. NotFound is tolerated (the store is simply created later). As with the goal store, symlinked directories are refused outright.","triggerScenarios":"Opening/validating the late usage store when the path exists but is a symlink (including Windows junctions/reparse points) or a non-directory entry.","commonSituations":"Users redirecting the usage store via symlink to another volume; junction points on Windows setups; leftover files at the store path from a crashed setup.","solutions":["Replace the symlink/junction with a real directory at the configured path","Remove the non-directory entry so the manager can create the store itself","Check Windows junction/reparse attributes if on Windows (fsutil / dir /AL) and rebuild the path normally"],"exampleFix":"// before\nmklink /D usage-store D:\\other\\usage\n// after\nmkdir usage-store  (or robocopy the contents and delete the junction)","handlingStrategy":"validation","validationCode":"let md = std::fs::metadata(&usage_dir)?;\nlet linked = md.file_type().is_symlink() || md.file_type() == std::fs::FileType::from(std::os::unix::fs::FileTypeExt::...) /* or on Windows check reparse */;\nif linked || !md.is_dir() { return Err(anyhow!(\"usage store must be a real directory\")); }","typeGuard":"fn is_real_dir_unix(p: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| !m.file_type().is_symlink() && m.is_dir()).unwrap_or(false)\n}","tryCatchPattern":"match manager.open_late_usage_store() {\n    Ok(s) => use(s),\n    Err(e) if e.to_string().contains(\"must be a real directory\") => eprintln!(\"The usage store path is a symlink/junction; recreate it as a real directory\"),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Do not redirect the usage store with symlinks or Windows junctions; move the configured root instead","On Windows, check for reparse points with fsutil before relocating store directories","Let the application create the store directory itself rather than pre-creating links"],"tags":["filesystem","symlink","validation","windows"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}