{"record":{"id":"2c0f9b3c514ea8ff","repo":"astrid-runtime/astrid","slug":"legacy-source-crosses-a-device-boundary","errorCode":null,"errorMessage":"legacy source crosses a device boundary: {}","messagePattern":"legacy source crosses a device boundary: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs","lineNumber":679,"sourceCode":"    if metadata.file_type().is_symlink() || !metadata.is_file() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"legacy source is not a regular file: {}\", path.display()),\n        ));\n    }\n    astrid_core::platform_fs::verify_no_redirects(path)?;\n    validate_private_entry(path, &metadata)?;\n    if active_mountpoint(path)? {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"legacy source is an active mount: {}\", path.display()),\n        ));\n    }\n    if let Some(parent) = path.parent()\n        && let Ok(parent_metadata) = fs::symlink_metadata(parent)\n        && device_id(&parent_metadata) != device_id(&metadata)\n    {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"legacy source crosses a device boundary: {}\",\n                path.display()\n            ),\n        ));\n    }\n    Ok(())\n}\n\nfn snapshot_dir(\n    root: &Path,\n    dir: &Path,\n    device: u64,\n    access: SourceAccess,\n    hasher: &mut blake3::Hasher,\n    identity: &mut SourceInventory,\n) -> io::Result<()> {","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs#L661-L697","documentation":"validate_source_path requires the source file to live on the same device as its parent directory (compared via device_id, i.e. st_dev). If they differ — meaning the entry sits on a different filesystem than its parent — the migration barrier treats it as crossing a device boundary and returns InvalidData, since safe in-place migration assumptions break across filesystems.","triggerScenarios":"Calling validate_source_path where the file's st_dev differs from its parent directory's st_dev — typically the file is itself a mountpoint or bind-mounted from another filesystem.","commonSituations":"A bind mount of a file from another volume over the expected path; the source placed on a tmpfs nested under a disk-backed directory; overlayfs upper/lower differences in containers.","solutions":["Move or copy the file onto the same filesystem as its parent directory, then validate the copy.","Remove the cross-device bind mount so the path is a normal file on the parent filesystem.","Choose a source location entirely within one filesystem."],"exampleFix":"// before\nlet src = Path::new(\"/var/astrid/state.lock\"); // tmpfs mounted at /var/astrid, parent on ext4\nvalidate_source_path(src)?;\n\n// after\nlet src = Path::new(\"/var/lib/astrid/state.lock\"); // same filesystem as its parent\nvalidate_source_path(src)?;","handlingStrategy":"validation","validationCode":"fn same_device_as_parent(path: &std::path::Path) -> std::io::Result<bool> {\n    use std::os::unix::fs::MetadataExt;\n    let md = std::fs::symlink_metadata(path)?;\n    let parent = path.parent().ok_or_else(|| std::io::Error::other(\"no parent\"))?;\n    let pmd = std::fs::symlink_metadata(parent)?;\n    Ok(md.dev() == pmd.dev())\n}\nassert!(same_device_as_parent(&src)?, \"source crosses a device boundary\");","typeGuard":"fn same_device(path: &std::path::Path) -> bool {\n    use std::os::unix::fs::MetadataExt;\n    match (std::fs::symlink_metadata(path), path.parent().and_then(|p| std::fs::symlink_metadata(p).ok())) {\n        (Ok(m), Some(p)) => m.dev() == p.dev(),\n        _ => false,\n    }\n}","tryCatchPattern":"if let Err(e) = validate_source_path(&src) {\n    if e.to_string().contains(\"device boundary\") {\n        let copy = std::path::Path::new(\"/var/lib/astrid/src-copy\");\n        std::fs::copy(&src, copy)?;\n        return validate_source_path(copy);\n    }\n    return Err(e);\n}","preventionTips":["Check st_dev equality between file and parent before validation.","Keep source files on the same filesystem as their directory.","Avoid nested tmpfs/bind mounts at source locations.","Use df <path> to confirm which filesystem hosts the file."],"tags":["filesystem","mount","device","rust"],"backgroundTag":"incompatible-source-type","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"}