{"record":{"id":"89f6e6c75bf51d24","repo":"astrid-runtime/astrid","slug":"migration-component-has-a-non-canonical-principal","errorCode":null,"errorMessage":"migration component has a non-canonical principal UID: {name}","messagePattern":"migration component has a non-canonical principal UID: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs","lineNumber":552,"sourceCode":"        \"system:state-db\"\n        | \"system:cow\"\n        | \"system:invites\"\n        | \"system:pair-tokens\"\n        | \"system:gateway-revocations\"\n        | \"system:host-secrets\"\n        | \"system:capsule-authority\"\n        | \"system:fresh-layout\" => return Ok(()),\n        _ => {},\n    }\n    let parts = name.split(':').collect::<Vec<_>>();\n    if parts.len() < 3 || parts[0] != \"principal\" {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"unknown migration component name: {name}\"),\n        ));\n    }\n    PrincipalUid::from_str(parts[1]).map_err(|_| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"migration component has a non-canonical principal UID: {name}\"),\n        )\n    })?;\n    let valid_shape = match parts[2] {\n        \"home\" | \"profile\" | \"capsules\" | \"secrets\" | \"audit\" | \"logs\" | \"tmp\" | \"distro-lock\"\n        | \"distro-init\" => parts.len() == 3,\n        \"env\" | \"secret\" => parts.len() == 4 && !parts[3].is_empty(),\n        _ => false,\n    };\n    if !valid_shape {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"unknown migration component name: {name}\"),\n        ));\n    }\n    Ok(())\n}","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs#L534-L570","documentation":"For `principal:` component names with the right part count, `validate_component_name` additionally parses `parts[1]` as a canonical `PrincipalUid`. This error is thrown when the UID segment cannot be parsed into a canonical `PrincipalUid`, meaning the ledger references a principal by a malformed, abbreviated, or non-canonical identifier.","triggerScenarios":"Calling `validate_ledger_shape` with a component name like `principal:alice:home` (alias instead of UID), `principal:0x123:home` (wrong format), or any `principal:<garbage>:<kind>` where `PrincipalUid::from_str(parts[1])` fails.","commonSituations":"Hand-written ledger entries substituting a human alias for the UID; a migration script truncating or reformatting UIDs; UIDs generated by an incompatible version with a different canonical encoding.","solutions":["Replace the second segment with the principal's canonical UID string as produced by `PrincipalUid` (canonical `from_str`/display form).","Look up the UID from the principal directory/alias binding rather than using the alias text.","Regenerate the ledger entry through the library so the UID is serialized canonically.","If the UID itself is corrupted and the principal no longer exists, remove the stale component entry and rebuild the ledger."],"exampleFix":"// before\n{\"name\": \"principal:alice:home\", ...}\n// after\n{\"name\": \"principal:01H8X7Y3Z9QWERTY:home\", ...}","handlingStrategy":"validation","validationCode":"// Pre-check that every principal component's UID segment parses canonically\nfn uid_is_canonical(name: &str) -> bool {\n    name.strip_prefix(\"principal:\")\n        .and_then(|r| r.split(':').next())\n        .map(|uid| PrincipalUid::from_str(uid).is_ok())\n        .unwrap_or(false)\n}","typeGuard":"fn parse_principal_uid(name: &str) -> Option<PrincipalUid> {\n    name.strip_prefix(\"principal:\")\n        .and_then(|r| r.split(':').next())\n        .and_then(|uid| PrincipalUid::from_str(uid).ok())\n}","tryCatchPattern":null,"preventionTips":["Resolve aliases to canonical UIDs via the principal directory before naming components","Never substitute human aliases or truncated IDs for UIDs in ledger entries","Round-trip UIDs through PrincipalUid::to_string to confirm canonical form","Generate ledger entries programmatically through the library API"],"tags":["migration","validation","identifier","rust"],"backgroundTag":"invalid-identifier-format","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"}