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

InvalidData

InvalidData

Error message

absent migration source has a digest: {}

What it means

validate_ledger_shape enforces the mirror invariant of the present/absent digest check: a component whose source IS present must not carry the sentinel digest "absent". A present source with an "absent" digest cannot be source-bound to any destination proof, so the ledger is rejected as InvalidData before it is written.

Source

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

        }
        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
                    .contains("layout-receipt=layout-v1-to-v2.complete")
            {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "CoW ledger component is not a source-bound verified discard",
                ));

View on GitHub (pinned to affd8760f4)

Solutions

  1. Compute and set the real content digest (e.g. blake3:...) for the present source
  2. If the source is genuinely gone, set source.present = false instead
  3. Re-run the migration step that produces the digest before writing the ledger

Example fix

// before
MigrationSource { present: true, digest: "absent", .. }
// after
MigrationSource { present: true, digest: "blake3:<computed>", .. }
Defensive patterns

Strategy: validation

Validate before calling

fn present_source_digest_ok(c: &MigrationComponent) -> bool {
    !c.source.present || c.source.digest != "absent"
}
let ok = ledger.components.iter().all(present_source_digest_ok);

Type guard

fn has_real_digest(c: &MigrationComponent) -> bool {
    !c.source.present || (c.source.digest != "absent" && c.source.digest.starts_with("blake3:"))
}

Try / catch

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

Prevention

When it happens

Trigger: Calling write_ledger (or any caller that validates ledger shape: reject_incomplete_layout_v2, retire_post_barrier_sources, resume_existing_layout) with a component entry where source.present == true but source.digest == "absent".

Common situations: Marking a component as migrated/present after the fact without recomputing the source digest; templated ledger generation that fills in present=true while leaving placeholder digests; a snapshot tool that copied the sentinel value into the wrong field.

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