astrid-runtime/astrid · error

legacy log receipt identity mismatch

Error message

legacy log receipt identity mismatch: {}

What it means

The legacy log import receipt on disk does not match the expected schema version or does not belong to the requested uid. The kernel requires every receipt to carry the current RECEIPT_SCHEMA and the same uid it was written for before it can be used as a destination proof. This prevents an old-format or misfiled receipt from being trusted during migration verification.

Solutions

  1. Delete or move aside the stale receipt so it is regenerated (an absent receipt is handled as "absent")
  2. Re-run the legacy log migration so a fresh receipt with the current schema and uid is written
  3. If upgrading, run any provided migration/upgrade command that rewrites receipts before verification
  4. Do not hand-edit or copy receipt files between uid paths
Defensive patterns

Strategy: validation

Validate before calling

// before relying on a receipt, check its schema/uid yourself
let receipt: serde_json::Value = serde_json::from_slice(&bytes)?;
if receipt["schema"] != EXPECTED_SCHEMA || receipt["uid"] != uid {
    // stale or foreign receipt: treat as absent and let it regenerate
    std::fs::remove_file(&path)?;
}

Type guard

fn is_current_receipt(r: &LogReceipt, uid: &str) -> bool {
    r.schema == RECEIPT_SCHEMA && r.uid == uid
}

Prevention

When it happens

Trigger: `legacy_log_destination_proof` reads the receipt at `receipt_path(home, uid)` and finds `receipt.schema != RECEIPT_SCHEMA` or `receipt.uid != uid`. Triggered by receipts written by an older kernel version, a receipt copied/renamed to another uid's path, or a hand-edited receipt.

Common situations: Upgrading the app across a schema-version bump while old receipts remain on disk; manually copying home directories between accounts/uids; restoring a partial backup where receipts and logs diverged.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/principal_log_migration.rs:89

/// The migration verifies every destination byte before publishing this
/// receipt. Runtime logs are appendable operational state, so later proof
/// checks bind the immutable receipt rather than re-hashing the live log tree.
///
/// # Errors
///
/// Returns an error when the receipt exists but is malformed or belongs to a
/// different UID.
pub(crate) fn legacy_log_destination_proof(
    home: &AstridHome,
    uid: PrincipalUid,
) -> io::Result<String> {
    let path = receipt_path(home, uid);
    let Some(bytes) = read_receipt_bytes(&path)? else {
        return Ok("absent".to_owned());
    };
    let receipt: LogReceipt = decode_receipt(&bytes, &path)?;
    if receipt.schema != RECEIPT_SCHEMA || receipt.uid != uid {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("legacy log receipt identity mismatch: {}", path.display()),
        ));
    }
    Ok(format!("blake3:{}", blake3::hash(&bytes).to_hex()))
}

fn migrate_one(
    home: &AstridHome,
    alias: &PrincipalId,
    uid: PrincipalUid,
    source: &Path,
) -> io::Result<()> {
    let receipt_path = receipt_path(home, uid);
    if let Some(bytes) = read_receipt_bytes(&receipt_path)? {
        let receipt: LogReceipt = decode_receipt(&bytes, &receipt_path)?;
        if receipt.schema != RECEIPT_SCHEMA || receipt.uid != uid || receipt.alias != *alias {
            return Err(conflict(

View on GitHub (pinned to affd8760f4)