{"record":{"id":"c049d9addfb5a0c5","repo":"astrid-runtime/astrid","slug":"legacy-principal-home-entry-crosses-a-filesystem-b","errorCode":null,"errorMessage":"legacy principal-home entry crosses a filesystem boundary: {}","messagePattern":"legacy principal-home entry crosses a filesystem boundary: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs","lineNumber":390,"sourceCode":"        Err(error) => return Err(error),\n    };\n    let root_device = device_id(&metadata);\n    let mut default_source_present = false;\n    astrid_core::platform_fs::verify_no_redirects(&root)?;\n    for entry in fs::read_dir(&root).map_err(io::Error::other)? {\n        let principal_root = entry.map_err(io::Error::other)?.path();\n        let principal_metadata = fs::symlink_metadata(&principal_root).map_err(io::Error::other)?;\n        if principal_metadata.file_type().is_symlink() || !principal_metadata.is_dir() {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy principal-home entry is not a regular directory: {}\",\n                    principal_root.display()\n                ),\n            ));\n        }\n        if device_id(&principal_metadata) != root_device {\n            return Err(io::Error::new(\n                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\");\n        match fs::symlink_metadata(&local_root) {\n            Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"legacy principal .local path is not a directory: {}\",\n                        local_root.display()\n                    ),\n                ));","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs#L372-L408","documentation":"preflight_legacy_audit_sources compares each principal-home entry's device id (st_dev) against the home root's device and aborts if they differ. A principal directory living on another filesystem cannot be safely covered by the same migration traversal/retirement assumptions, so the migration stops with InvalidData before opening any audit source.","triggerScenarios":"migrate_legacy_audit with a principal directory under the home root that resides on a different mounted filesystem than the home root itself (device_id(&principal_metadata) != root_device, host_fs.rs:389) — e.g. a separate mount or bind mount for one principal.","commonSituations":"Mounting a dedicated disk/NFS share for one user's data under the home root; containers bind-mounting a volume at a principal directory path; encrypted-home setups mounting at a subdirectory.","solutions":["Unmount the separate filesystem mounted at the reported principal path (umount) and re-run the migration.","Move the principal's data onto the same filesystem as the home root and remove the mount.","Keep the extra mount, but relocate it outside the principal home root tree.","If using containers, stop bind-mounting volumes at per-principal paths inside the home root during migration."],"exampleFix":"// before\n/dev/sdb1 on /home/u/principal-7f3a type ext4\n// after\n$ umount /home/u/principal-7f3a && mv /mnt/sdb1-data/principal-7f3a /home/u/","handlingStrategy":"validation","validationCode":"use std::os::unix::fs::MetadataExt;\nfn principals_on_root_device(root: &std::path::Path) -> std::io::Result<Vec<std::path::PathBuf>> {\n    let root_dev = std::fs::symlink_metadata(root)?.dev();\n    let mut bad = Vec::new();\n    for e in std::fs::read_dir(root)? {\n        let p = e?.path();\n        if std::fs::symlink_metadata(&p)?.dev() != root_dev {\n            bad.push(p);\n        }\n    }\n    Ok(bad) // must be empty before migrating\n}","typeGuard":"fn on_same_fs(root_dev: u64, p: &std::path::Path) -> bool {\n    std::os::unix::fs::MetadataExt::dev(&std::fs::symlink_metadata(p).ok()?) == root_dev\n}","tryCatchPattern":"match result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains(\"filesystem boundary\") => {\n        // unmount or relocate the reported principal dir, then retry\n    }\n    other => other?,\n}","preventionTips":["Keep all principal directories on the same filesystem as the home root.","Check with `df <path>` / st_dev comparison before scheduling the migration.","Mount extra volumes outside the principal home tree."],"tags":["filesystem","mount","migration","device"],"backgroundTag":"unsupported-operation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}