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
- Compute and set the real content digest (e.g. blake3:...) for the present source
- If the source is genuinely gone, set source.present = false instead
- 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
- Compute the source digest at the moment the source is marked present
- Reject placeholder digests early in any ledger-building code
- Keep digest computation and source presence in one code path
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
- absent migration source has a digest: {}
- principal migration component has no ordinary-home receipt:
- required system migration receipt is missing: {}
- live principal migration receipt is missing: {}
- unknown migration component name: {name}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ec7b3e5478508b97.
Report an issue: GitHub.