{"record":{"id":"3b6544f7e94d9ffc","repo":"astrid-runtime/astrid","slug":"legacy-principal-alias-has-no-durable-uid","errorCode":null,"errorMessage":"legacy principal {alias} has no durable UID","messagePattern":"legacy principal (.+?) has no durable UID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/mod.rs","lineNumber":117,"sourceCode":"            )\n        })?;\n        // A migration receipt is immutable provenance and takes precedence\n        // over the mutable alias directory. If an alias was deleted and then\n        // reused, never stream the surviving old source into the replacement\n        // UID; fail closed instead.\n        let receipt_uid = receipt_uid_for_alias(home, &alias)?;\n        let live_uid = principals.uid_for(&alias).ok();\n        let uid = match (receipt_uid, live_uid) {\n            (Some(receipt), Some(live)) if receipt != live => {\n                return Err(conflict_path(\n                    &entry.path(),\n                    \"migration receipt UID differs from the live alias binding\",\n                ));\n            },\n            (Some(receipt), _) => receipt,\n            (None, Some(live)) => live,\n            (None, None) => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"legacy principal {alias} has no durable UID\"),\n                ));\n            },\n        };\n        migrate_one_principal(home, store, uid, &alias, &entry.path())?;\n    }\n    Ok(())\n}\n\n/// Snapshot only ordinary entries owned by this migration.\n///\n/// Dedicated subtrees (capsules, env, audit, logs, and other released\n/// operational paths) are excluded because their own migrations bind and\n/// retire them. The fields use the same digest/count/byte inventory as the\n/// ordinary-home receipt. `present` distinguishes an absent source from an\n/// existing source containing only dedicated entries.\n#[derive(Clone, Debug, PartialEq, Eq)]","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/mod.rs#L99-L135","documentation":"Raised when neither a migration receipt nor a live alias binding provides a durable UID for a legacy principal. Migration needs a stable UID to associate the principal's data; without one it cannot proceed and returns InvalidData.","triggerScenarios":"Calling migrate_legacy_principal_homes when for a given alias both the migration receipt and the live alias binding are absent (the (None, None) match arm), e.g. the receipt file was deleted and the alias store has no record.","commonSituations":"Partially cleaned state after manual deletion of receipts, a store that was reset while legacy directories remain, or migration re-run against a pruned store.","solutions":["Restore the missing receipt or live alias binding (re-register the principal)","Reconstruct the durable UID from backups of the alias store","Remove the orphaned legacy principal directory if the principal no longer exists","Re-run migration once a durable UID is available"],"exampleFix":"// before: orphaned legacy dir aborts migration\nmigrate_legacy_principal_homes(&home, &store)?;\n// after: re-register or remove the orphan first\nensure_principal_registered(&store, &alias)?;\nmigrate_legacy_principal_homes(&home, &store)?;","handlingStrategy":"validation","validationCode":"fn principal_resolvable(store: &Store, alias: &str) -> bool {\n    store.receipt_for(alias).is_some() || store.live_binding(alias).is_some()\n}","typeGuard":"fn has_durable_uid(r: Option<&Receipt>, l: Option<&Binding>) -> bool { r.is_some() || l.is_some() }","tryCatchPattern":"match migrate_legacy_principal_homes(&home, &store) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        re_register_orphaned_principals(&store)?;\n        migrate_legacy_principal_homes(&home, &store)\n    }\n    other => other,\n}","preventionTips":["Never delete migration receipts independently of the alias store","Back up the alias store before touching legacy directories","Re-register principals whose live bindings were removed before migrating"],"tags":["migration","identity","missing-data"],"backgroundTag":"missing-required-config-field","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}