astrid-runtime/astrid · error
legacy source crosses a mount or device boundary: {}
Error message
legacy source crosses a mount or device boundary: {} What it means
During legacy tree retirement, retire_tree walks each child of the legacy source directory and refuses to descend into any child that is itself an active mountpoint or whose st_dev differs from the parent directory's device. The migration path intentionally only sweeps regular files and directories that live wholly on one filesystem, because a bind mount or cross-device subtree cannot be safely renamed/removed as part of the same atomic retirement. The path of the offending child is interpolated into the message.
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:295
io::ErrorKind::InvalidData,
format!(
"legacy component source reappeared during ordinary retirement: {}",
child.display()
),
));
}
let child_meta = fs::symlink_metadata(&child).map_err(io::Error::other)?;
if child_meta.file_type().is_symlink() || (!child_meta.is_file() && !child_meta.is_dir()) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"legacy source contains redirect or special entry: {}",
child.display()
),
));
}
if active_mountpoint(&child)? || device_id(&child_meta) != device {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"legacy source crosses a mount or device boundary: {}",
child.display()
),
));
}
if child_meta.is_dir() {
let child_snapshot = snapshot_path(&child)?;
retire_tree(&child, &child_snapshot, protected)?;
} else {
astrid_core::platform_fs::verify_no_redirects(&child)?;
let leaf_snapshot = snapshot_path(&child)?;
if leaf_snapshot.entries != 1 || leaf_snapshot.bytes != child_meta.len() {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"legacy source changed before retirement: {}",View on GitHub (pinned to affd8760f4)
Solutions
- Unmount (umount) the mountpoint located at the reported child path before re-running the migration.
- Move the mounted data off the legacy tree (e.g. mount elsewhere and use a symlink outside the migrated root), then re-run migration.
- If the mount is stale, remove it from /etc/fstab or the container mount configuration so it no longer appears in the tree.
- If the child is merely on another device but not mounted, consolidate it onto the same filesystem as its parent.
Example fix
// before: bind mount inside legacy source /home/u/.local/share/cache on /home/u/.local/audit type tmpfs // after $ umount /home/u/.local/audit $ astrid migrate-legacy-audit # proceeds past the boundary check
Defensive patterns
Strategy: validation
Validate before calling
use std::os::unix::fs::MetadataExt;
fn precheck_child_mounts(root: &std::path::Path) -> std::io::Result<()> {
let root_dev = std::fs::symlink_metadata(root)?.dev();
for e in std::fs::read_dir(root)? {
let p = e?.path();
let m = std::fs::symlink_metadata(&p)?;
if m.is_dir() && m.dev() != root_dev {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("cross-device child: {}", p.display()),
));
}
}
Ok(())
} Type guard
fn same_device(parent_dev: u64, child: &std::path::Path) -> std::io::Result<bool> {
Ok(std::os::unix::fs::MetadataExt::dev(&std::fs::symlink_metadata(child)?) == parent_dev)
} Try / catch
match result {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("mount or device boundary") => {
// prompt user to unmount the reported path, then retry
}
other => other?,
} Prevention
- Keep mountpoints out of the legacy home/.local tree before migrating.
- Scan for mounts with findmnt --target or mountpoint(1) inside the migration root beforehand.
- In containers, avoid bind-mounting volumes at per-principal or .local paths.
When it happens
Trigger: Calling migrate_legacy_audit (which reaches retire_tree via retire_post_barrier_sources) while a direct child of the legacy principal-home tree is a mountpoint, a bind mount, an NFS/overlay mount, or a directory on a different device (st_dev differs from the parent). Also triggered by tests retirement_rejects_source_mutation_and_preserves_data-style harnesses that mount something inside the source tree.
Common situations: Users who mount a network share, tmpfs, or extra disk under ~/.local or the principal home directory; container images with overlayfs bind mounts inside the home; developers testing with mounted fixtures inside the legacy source.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- FUSE mount completed but is absent from the Linux mount tabl
- legacy source is an active mount: {}
- legacy principal-home entry crosses a filesystem boundary: {
- legacy audit tree crosses a filesystem or mount boundary: {}
- legacy capsule directory is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/c442253d5c3ce841.
Report an issue: GitHub.