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

  1. Replace the second segment with the principal's canonical UID string as produced by `PrincipalUid` (canonical `from_str`/display form).
  2. Look up the UID from the principal directory/alias binding rather than using the alias text.
  3. Regenerate the ledger entry through the library so the UID is serialized canonically.
  4. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/89f6e6c75bf51d24. Report an issue: GitHub.