astrid-runtime/astrid · error · io::Error

layout migration receipt destination path does not match the

Error message

layout migration receipt destination path does not match the live Astrid volume

What it means

After resolving the live path's canonical parent and file name, verify_receipt_destination_is_live_path compares it against the receipt's physical_path(destination). If the receipt's recorded destination differs from the canonicalized live volume path, this InvalidData error is raised: the migration receipt does not describe the volume currently in use.

Source

Thrown at crates/astrid-core/src/dirs_layout_records.rs:212

pub(super) fn verify_receipt_destination_is_live_path(
    destination: &LayoutTreeIdentityV1,
    live_path: &Path,
) -> io::Result<()> {
    let parent = live_path.parent().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            "live Astrid volume has no parent",
        )
    })?;
    let file_name = live_path.file_name().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            "live Astrid volume has no name",
        )
    })?;
    let canonical_parent = std::fs::canonicalize(parent)?;
    if physical_path(destination)? != canonical_parent.join(file_name) {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "layout migration receipt destination path does not match the live Astrid volume",
        ));
    }
    verify_receipt_destination_authority(destination)
}

fn physical_path(destination: &LayoutTreeIdentityV1) -> io::Result<PathBuf> {
    let bytes = hex::decode(&destination.physical_path_hex)
        .map_err(|error| io::Error::other(format!("decode layout destination path: {error}")))?;
    encoded_bytes_to_os_string(bytes).map(PathBuf::from)
}

#[cfg(unix)]
#[expect(
    clippy::unnecessary_wraps,
    reason = "the cross-platform receipt decoder has one fallible signature"
)]

View on GitHub (pinned to affd8760f4)

Solutions

  1. Regenerate the layout migration receipt so its destination identity matches the canonical live path.
  2. Restore the volume file to the exact path recorded in the receipt.
  3. Diff the receipt's destination against `readlink -f <live_path>` and reconcile whichever is stale.

Example fix

// before: receipt written for old location
// destination = /mnt/data/old/volume.bin
// after: move volume back or rewrite receipt
std::fs::rename("/mnt/data/new/volume.bin", "/mnt/data/old/volume.bin")?;
Defensive patterns

Strategy: validation

Validate before calling

let canonical = std::fs::canonicalize(&live_path)?;
if canonical != /* path from receipt */ {
    return Err("receipt destination does not match live volume; reissue receipt");
}

Try / catch

match retire_verified_legacy_source(...) {
    Err(e) if e.to_string().contains("does not match the live Astrid volume") => regenerate_receipt_and_retry(),
    other => other,
}

Prevention

When it happens

Trigger: Called via retire_verified_legacy_source when canonical_parent.join(file_name) != physical_path(destination) — e.g. the volume was moved/renamed after the receipt was written, or the receipt records a stale or different destination identity.

Common situations: Volume file moved to a new mount point or renamed; receipt copied from another machine or volume; a symlinked directory canonicalizes to a different real location than the receipt recorded.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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