{"record":{"id":"b8b4e201d84d6117","repo":"astrid-runtime/astrid","slug":"destination-receipt-changed-for-migration-componen","errorCode":null,"errorMessage":"destination receipt changed for migration component {}","messagePattern":"destination receipt changed for migration component (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs","lineNumber":415,"sourceCode":"    }\n    for component in &existing.components {\n        if mutable_component(&component.name) {\n            continue;\n        }\n        if let Some(uid) = principal_component_uid(&component.name)\n            && !principal_homes.contains(&uid)\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"principal migration component has no ordinary-home receipt: {}\",\n                    component.name\n                ),\n            ));\n        }\n        if let Some(current) = proofs.get(&component.name) {\n            if current != &component.destination_proof {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"destination receipt changed for migration component {}\",\n                        component.name\n                    ),\n                ));\n            }\n            continue;\n        }\n        if component.name.starts_with(\"system:\") {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"required system migration receipt is missing: {}\",\n                    component.name\n                ),\n            ));\n        }","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs#L397-L433","documentation":"When revalidating an existing layout, each immutable component's currently stored proof is compared against the destination_proof expected from the ledger. If they differ, this error is thrown: the recorded receipt for that component changed since migration, which breaks the immutability guarantee, so resume fails closed.","triggerScenarios":"validate_existing_proofs (via resume_existing_layout or existing_layout_requires_receipts_for_live_immutable_components) reads proofs from the ledger and finds a mismatch with component.destination_proof — e.g. the receipt file on disk was overwritten, or component metadata changed after the ledger was written.","commonSituations":"Someone edited or re-wrote a receipt in migrations_dir; the ledger and receipts got out of sync after a partial restore or an interrupted re-migration; running a newer library version that recomputed proofs with different content.","solutions":["Diff the current receipt against component.destination_proof in the ledger to see what changed.","Restore the original receipt from backup, or delete both ledger and receipts for a full clean re-migration.","Never regenerate receipts by hand; use the migration entry points so proofs and ledger stay consistent."],"exampleFix":"// before: receipt file overwritten externally\n$ cp <backup>/migrations/<component>.json <migrations-dir>/<component>.json\n// after (if no backup): clean re-migration\n$ rm -r <migrations-dir> && astrid migrate","handlingStrategy":"validation","validationCode":"let stored = std::fs::read_to_string(proof_path)?;\nif stored != component.destination_proof {\n    eprintln!(\"receipt drift detected for {}; restore from backup\", component.name);\n}","typeGuard":null,"tryCatchPattern":"match resume_existing_layout(...) {\n    Err(e) if e.to_string().contains(\"destination receipt changed\") => {\n        eprintln!(\"receipt was modified externally; restore backup or full re-migrate\");\n    }\n    r => r?,\n}","preventionTips":["Make migrations_dir read-only to non-root/non-user after migration.","Treat any receipt drift as tampering and investigate before re-migrating.","Regenerate ledger and receipts together; never rewrite one side only."],"tags":["security","ledger","tampering","migration"],"backgroundTag":"checksum-mismatch","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"}