astrid-runtime/astrid · error
migration component has a non-canonical principal UID: {name
Error message
migration component has a non-canonical principal UID: {name} What it means
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.
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs:552
"system:state-db"
| "system:cow"
| "system:invites"
| "system:pair-tokens"
| "system:gateway-revocations"
| "system:host-secrets"
| "system:capsule-authority"
| "system:fresh-layout" => return Ok(()),
_ => {},
}
let parts = name.split(':').collect::<Vec<_>>();
if parts.len() < 3 || parts[0] != "principal" {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown migration component name: {name}"),
));
}
PrincipalUid::from_str(parts[1]).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("migration component has a non-canonical principal UID: {name}"),
)
})?;
let valid_shape = match parts[2] {
"home" | "profile" | "capsules" | "secrets" | "audit" | "logs" | "tmp" | "distro-lock"
| "distro-init" => parts.len() == 3,
"env" | "secret" => parts.len() == 4 && !parts[3].is_empty(),
_ => false,
};
if !valid_shape {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown migration component name: {name}"),
));
}
Ok(())
}View on GitHub (pinned to affd8760f4)
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.
Example fix
// before
{"name": "principal:alice:home", ...}
// after
{"name": "principal:01H8X7Y3Z9QWERTY:home", ...} Defensive patterns
Strategy: validation
Validate before calling
// Pre-check that every principal component's UID segment parses canonically
fn uid_is_canonical(name: &str) -> bool {
name.strip_prefix("principal:")
.and_then(|r| r.split(':').next())
.map(|uid| PrincipalUid::from_str(uid).is_ok())
.unwrap_or(false)
} Type guard
fn parse_principal_uid(name: &str) -> Option<PrincipalUid> {
name.strip_prefix("principal:")
.and_then(|r| r.split(':').next())
.and_then(|uid| PrincipalUid::from_str(uid).ok())
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- corpus label must contain only lowercase ASCII letters, digi
- legacy capsule entry has a non-UTF-8 name
- unknown migration component name: {name}
- migration ledger contains duplicate component: {}
- migration ledger components are not canonically sorted
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/89f6e6c75bf51d24.
Report an issue: GitHub.