astrid-runtime/astrid · error

absent migration source has a digest: {}

Error message

absent migration source has a digest: {}

What it means

validate_ledger_shape enforces that every migration ledger component's source presence flag is consistent with its digest. A component whose source is not present must carry the literal digest "absent"; any other digest is a self-contradictory record. This is an InvalidData io::Error raised during ledger admission before any migration state is written.

Source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs:642

            .is_some_and(|previous: &String| previous >= &component.name)
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "migration ledger components are not canonically sorted",
            ));
        }
        previous = Some(component.name.clone());
        if component.source.present && component.source.digest == "absent" {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "present migration source has absent digest: {}",
                    component.name
                ),
            ));
        }
        if !component.source.present && component.source.digest != "absent" {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("absent migration source has a digest: {}", component.name),
            ));
        }
        if component.destination_proof.is_absent() && component.source.present {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("invalid destination proof for component {}", component.name),
            ));
        }
        if component.name == "system:cow" {
            let expected = format!("source-digest={}", component.source.digest);
            if !component
                .destination_proof
                .starts_with("verified-discard-v1:")
                || !component.destination_proof.contains(&expected)
                || !component
                    .destination_proof

View on GitHub (pinned to affd8760f4)

Solutions

  1. Set source.digest to the literal string "absent" whenever source.present is false
  2. If the source actually exists, set source.present = true and keep the real digest
  3. Regenerate the ledger entry with the library's own writer instead of constructing it by hand

Example fix

// before
MigrationSource { present: false, digest: "blake3:abc123", .. }
// after
MigrationSource { present: false, digest: "absent", .. }
Defensive patterns

Strategy: validation

Validate before calling

fn source_absent_digest_ok(c: &MigrationComponent) -> bool {
    c.source.present || c.source.digest == "absent"
}
// validate all components before calling write_ledger
let ok = ledger.components.iter().all(source_absent_digest_ok);

Type guard

fn has_consistent_source(c: &MigrationComponent) -> bool {
    if c.source.present { c.source.digest != "absent" } else { c.source.digest == "absent" }
}

Try / catch

match write_ledger(&ledger) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("absent migration source has a digest") => {
        // fix source.present/digest pairing and retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling write_ledger (directly or via reject_incomplete_layout_v2, retire_post_barrier_sources, or resume_existing_layout, or the ledger-shape tests) with a MigrationLedger containing a component where source.present == false but source.digest is set to a real digest value instead of "absent".

Common situations: Hand-edited or programmatically generated ledger JSON where an author zeroes out the source but forgets to reset the digest; migrating ledgers between schema versions that changed the sentinel convention; copying a component entry and clearing present without clearing digest.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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