astrid-runtime/astrid · error

legacy source is an active mount: {}

Error message

legacy source is an active mount: {}

What it means

retire_tree refuses to delete a legacy source that is itself an active mount point, detected via /proc/self/mountinfo (Linux) or statfs filesystem-id comparison (macOS). Deleting a mounted directory would only clear the mountpoint while leaving the mounted data, so the operation aborts with InvalidData.

Source

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

            io::ErrorKind::AlreadyExists,
            format!(
                "legacy source changed before retirement: {}",
                path.display()
            ),
        ));
    }
    let metadata = fs::symlink_metadata(path)?;
    if !metadata.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "legacy retirement root is not a directory: {}",
                path.display()
            ),
        ));
    }
    if active_mountpoint(path)? {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("legacy source is an active mount: {}", path.display()),
        ));
    }
    let device = device_id(&metadata);
    for entry in fs::read_dir(path).map_err(io::Error::other)? {
        let child = entry.map_err(io::Error::other)?.path();
        if protected.iter().any(|candidate| candidate == &child) {
            return Err(io::Error::new(
                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()) {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Unmount the mount point (`umount <path>`) so the legacy path is an ordinary directory again, then rerun retirement.
  2. Move the mount elsewhere; the legacy path must live on the same filesystem as its parent with no mount at or below it.
  3. If the data genuinely lives on the other volume, migrate it physically into the legacy location (or update config) instead of mounting.
  4. After unmounting, verify with `findmnt <path>` that nothing is mounted before retrying.

Example fix

# before
mount --bind /data/legacy ~/.astrid/legacy/db
// after
umount ~/.astrid/legacy/db
rsync -a /data/legacy/ ~/.astrid/legacy/db/   # physical copy, then migrate
Defensive patterns

Strategy: validation

Validate before calling

// Linux: check the path is not a mount point before migrating
fn is_mountpoint(path: &std::path::Path) -> std::io::Result<bool> {
    let canon = std::fs::canonicalize(path)?;
    let mi = std::fs::read_to_string("/proc/self/mountinfo")?;
    Ok(mi.lines().any(|l| l.split_whitespace().nth(4) == Some(canon.to_str().unwrap_or(""))))
}

Try / catch

match retire_tree(path, &expected, &protected) {
    Err(e) if e.to_string().contains("is an active mount") => {
        eprintln!("unmount {} before migration", path.display());
    }
    other => other?,
}

Prevention

When it happens

Trigger: retire_tree -> active_mountpoint(path) returns true because the legacy path is a bind mount, tmpfs, network mount, or (macOS) resides on a different filesystem than its parent. This check runs at the root and recursively at each child (children on a different device raise the sibling boundary error).

Common situations: Users bind-mounting or symlinking-via-mount legacy data from another volume into the old location; Docker/overlay or NFS mounts placed at the legacy path; macOS users whose home is on a different volume than the mount point's parent.

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/67a825776458ed1a. Report an issue: GitHub.