{"record":{"id":"7c8bf8cd8d178906","repo":"astrid-runtime/astrid","slug":"legacy-principal-home-entry-is-not-a-regular-direc-7c8bf8","errorCode":null,"errorMessage":"legacy principal-home entry is not a regular directory: {principal_root}","messagePattern":"legacy principal-home entry is not a regular directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":4143,"sourceCode":"                format!(\n                    \"legacy principal-home root is not a directory: {}\",\n                    root.display()\n                ),\n            ));\n        },\n        Ok(metadata) => metadata,\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),\n        Err(error) => return Err(error),\n    };\n    let root_device = audit_tree_device(&metadata);\n    let mut default_source_present = false;\n    astrid_core::platform_fs::verify_no_redirects(&root)?;\n    for entry in std::fs::read_dir(&root)? {\n        let entry = entry?;\n        let principal_root = entry.path();\n        let principal_metadata = std::fs::symlink_metadata(&principal_root)?;\n        if principal_metadata.file_type().is_symlink() || !principal_metadata.is_dir() {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy principal-home entry is not a regular directory: {}\",\n                    principal_root.display()\n                ),\n            ));\n        }\n        if audit_tree_device(&principal_metadata) != root_device {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy principal-home entry crosses a filesystem boundary: {}\",\n                    principal_root.display()\n                ),\n            ));\n        }\n        astrid_core::platform_fs::verify_no_redirects(&principal_root)?;\n        let local_root = principal_root.join(\".local\");","sourceCodeStart":4125,"sourceCodeEnd":4161,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L4125-L4161","documentation":"During the legacy principal-home audit, each entry directly under the home root must be a regular directory (checked with `symlink_metadata`, so symlinks are rejected even if they point to directories). An entry such as a per-principal directory that is a symlink, file, or other non-directory type triggers this InvalidData error, after `verify_no_redirects` already cleared the root path itself.","triggerScenarios":"Running the audit when any entry under the home root (e.g. a principal's directory) is a symlink or a regular file instead of a directory — detected via `symlink_metadata(...).file_type().is_symlink() || !is_dir()`.","commonSituations":"Dotfile/sync managers symlinking principal directories to a data volume; a corrupt or partial restore leaving a file where a principal directory should be; manually moving principal data to another disk with a symlink left behind.","solutions":["Replace each offending symlink with a real directory (move the data back or use a bind mount instead of a symlink).","Remove or relocate stray files that occupy principal-directory names.","Exclude/fix sync tools (Dropbox, stow, dotbot) that convert directories into symlinks.","Re-run `verify_no_redirects`/the audit after entries are plain directories."],"exampleFix":"// before: principal dir is a symlink\n~/.astrid/principals/alice -> /mnt/secrets/alice\n// after: real directory\nrm ~/.astrid/principals/alice\nmkdir ~/.astrid/principals/alice && cp -a /mnt/secrets/alice/. ~/.astrid/principals/alice/","handlingStrategy":"validation","validationCode":"for entry in std::fs::read_dir(home.home_dir())? {\n    let p = entry?.path();\n    let md = std::fs::symlink_metadata(&p)?;\n    if md.file_type().is_symlink() || !md.is_dir() {\n        eprintln!(\"{} must be a plain directory\", p.display());\n    }\n}","typeGuard":"fn entries_are_plain_dirs(root: &std::path::Path) -> std::io::Result<bool> {\n    Ok(std::fs::read_dir(root)?.try_all(|e| {\n        let md = std::fs::symlink_metadata(e.path())?;\n        Ok(!md.file_type().is_symlink() && md.is_dir())\n    })?)\n}","tryCatchPattern":"match run_legacy_home_audit(&home) {\n    Err(e) if e.to_string().contains(\"entry is not a regular directory\") => {\n        eprintln!(\"a home entry is a symlink/file; materialize it as a real directory and retry\");\n    }\n    other => other?,\n}","preventionTips":["Exclude the Astrid home tree from dotfile/sync managers that create symlinks.","Use bind mounts instead of symlinks to relocate principal directories.","After restores, verify every entry under the home root is a plain directory before running the audit."],"tags":["migration","symlink","filesystem","security"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}