{"record":{"id":"86b7382eba1fa102","repo":"astrid-runtime/astrid","slug":"present-migration-source-has-absent-digest","errorCode":null,"errorMessage":"present migration source has absent digest: {}","messagePattern":"present migration source has absent digest: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs","lineNumber":633,"sourceCode":"                io::ErrorKind::InvalidData,\n                format!(\n                    \"migration ledger contains duplicate component: {}\",\n                    component.name\n                ),\n            ));\n        }\n        if previous\n            .as_ref()\n            .is_some_and(|previous: &String| previous >= &component.name)\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"migration ledger components are not canonically sorted\",\n            ));\n        }\n        previous = Some(component.name.clone());\n        if component.source.present && component.source.digest == \"absent\" {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"present migration source has absent digest: {}\",\n                    component.name\n                ),\n            ));\n        }\n        if !component.source.present && component.source.digest != \"absent\" {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"absent migration source has a digest: {}\", component.name),\n            ));\n        }\n        if component.destination_proof.is_absent() && component.source.present {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"invalid destination proof for component {}\", component.name),\n            ));","sourceCodeStart":615,"sourceCodeEnd":651,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs#L615-L651","documentation":"For each ledger component, a present source must carry a real content digest; the sentinel value `\"absent\"` is reserved for sources that were verified not to exist. This error is thrown when a component claims `source.present == true` while its `source.digest` is still the `\"absent\"` sentinel — a self-contradictory ledger entry.","triggerScenarios":"Calling `validate_ledger_shape` (via `write_ledger` or the decode/validate paths) on a `MigrationLedger` where a component has `source: {\"present\": true, \"digest\": \"absent\"}` — typically from hand-assembled JSON or a builder that set `present` without recomputing the digest.","commonSituations":"Hand-editing `present` from `false` to `true` without updating the digest; a script template filling in `present: true` while leaving the placeholder digest; partially applied migration steps where the digest update was lost.","solutions":["Recompute the real source digest for the component's source path and write it into `source.digest`, keeping `present: true` only if the source actually exists.","If the source truly does not exist, set `present: false` and `digest: \"absent\"` instead.","Regenerate the component entry (and its destination proof) via the library's migration path rather than editing the JSON directly.","Re-run the migration resume after the fix so all shape invariants are re-validated."],"exampleFix":"// before\n{\"name\": \"principal:01H8X:home\", \"source\": {\"present\": true, \"digest\": \"absent\"}}\n// after\n{\"name\": \"principal:01H8X:home\", \"source\": {\"present\": true, \"digest\": \"sha256:9f2c...\"}}","handlingStrategy":"validation","validationCode":"// Pre-flight: present sources carry a real digest, absent sources use the sentinel\nfn source_digest_consistent(c: &MigrationComponent) -> bool {\n    if c.source.present { c.source.digest != \"absent\" } else { c.source.digest == \"absent\" }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set `present` and `digest` together from a single computation of the source identity","Never flip `present` in JSON without recomputing the digest","Let the library derive SourceIdentity from the filesystem instead of templating it","Dry-run validate_ledger_shape on generated ledgers before writing them"],"tags":["migration","validation","invariant","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}