astrid-runtime/astrid · error

legacy audit tree crosses a filesystem or mount boundary: {}

Error message

legacy audit tree crosses a filesystem or mount boundary: {}

What it means

validate_audit_tree enforces that the whole legacy audit tree lives on one filesystem: each node's device id must equal the root device and the node must not itself be an active mountpoint. A violation means part of the tree sits on another mount (bind mount, network share, tmpfs), so a same-filesystem rename-based retirement would be incomplete or could silently move only part of the data.

Source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:523

        Err(error) => return Err(error),
    }
    Ok(())
}

#[cfg(not(unix))]
fn validate_audit_tree(path: &Path, root_device: u64) -> io::Result<()> {
    let metadata = fs::symlink_metadata(path)?;
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "legacy audit tree is redirected or not a directory: {}",
                path.display()
            ),
        ));
    }
    if device_id(&metadata) != root_device || active_mountpoint(path)? {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "legacy audit tree crosses a filesystem or mount boundary: {}",
                path.display()
            ),
        ));
    }
    astrid_core::platform_fs::verify_no_redirects(path)?;
    for entry in fs::read_dir(path).map_err(io::Error::other)? {
        let child = entry.map_err(io::Error::other)?.path();
        let child_metadata = fs::symlink_metadata(&child).map_err(io::Error::other)?;
        if child_metadata.file_type().is_symlink()
            || device_id(&child_metadata) != root_device
            || active_mountpoint(&child)?
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(

View on GitHub (pinned to affd8760f4)

Solutions

  1. Unmount the mount covering the audit path (umount) so the tree is a single filesystem, then re-run migration.
  2. Move the audit data onto the same filesystem as the principal home and remove the external mount.
  3. If the data must stay on another filesystem, consolidate it into the default location before migration.

Example fix

// before
mount --bind /mnt/data/audit ~/.astrid/principal/audit
// after
umount ~/.astrid/principal/audit
# data lives directly on the home filesystem
Defensive patterns

Strategy: validation

Validate before calling

fn same_device(root: &Path) -> std::io::Result<bool> {
    use std::os::unix::fs::MetadataExt;
    let r = std::fs::symlink_metadata(root)?;
    for entry in std::fs::read_dir(root)? {
        if std::fs::symlink_metadata(entry?.path())?.dev() != r.dev() { return Ok(false); }
    }
    Ok(true)
}

Try / catch

if let Err(e) = migrate_legacy_audit(&home, &source) {
    if e.to_string().contains("filesystem or mount boundary") {
        // findmnt to locate the mount, unmount it, then retry
    }
}

Prevention

When it happens

Trigger: Any validate_audit_tree caller (preflight, retire, recursive descent, delete) encountering a node whose st_dev differs from the root device, or a path that is an active mountpoint — e.g. the audit dir or a subtree is bind-mounted or on a separate partition/NFS mount.

Common situations: Users mounting a separate disk or NFS share at ~/.astrid/audit; bind mounts in containers; tmpfs mounted over part of the tree; encrypted-home setups mounting subtrees.

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/c1211cdf800d8931. Report an issue: GitHub.