astrid-runtime/astrid · error
principal has an invalid genesis public key
Error message
principal {alias} has an invalid genesis public key: {error} What it means
Thrown by genesis_public_key_bytes when the selected device key's hex string cannot be decoded into an astrid_crypto::PublicKey via from_hex. The profile selected a key (so one exists) but its pubkey bytes are not a valid Ed25519 public key in hex — wrong length or non-hex characters. The underlying decode error is embedded in the message.
Solutions
- Fix the device.pubkey field so it contains a valid Ed25519 public key as plain hex of the expected length.
- Re-export the profile from the legacy application so it writes a correct key.
- Remove/quarantine the principal if the key cannot be recovered, instead of minting it.
- Validate profile keys with PublicKey::from_hex before starting migration to catch bad entries early.
Example fix
// before "pubkey": "0xAB12CD..." // 0x prefix, invalid // after "pubkey": "ab12cd34..." // plain lowercase hex, 64 chars for Ed25519
Defensive patterns
Strategy: validation
Validate before calling
fn pubkey_is_valid_hex(s: &str) -> bool {
astrid_crypto::PublicKey::from_hex(s).is_ok()
} Try / catch
match mint_valid_leftover(entry) {
Err(e) if e.to_string().contains("invalid genesis public key") => {
// re-export or quarantine the principal with the bad key
quarantine(entry)?;
}
other => other?,
} Prevention
- Pre-validate all device pubkey hex strings with from_hex before migration.
- Store keys as plain lowercase hex without 0x prefixes or whitespace.
- Re-export profiles from the legacy writer instead of manual edits.
- Confirm all stored keys are Ed25519 of the expected byte length.
When it happens
Trigger: genesis_public_key_bytes (via mint_valid_leftover) calls PublicKey::from_hex(&device.pubkey) on the earliest device key and hex decoding or key-length validation fails.
Common situations: A profile with a truncated or padded pubkey string from a buggy legacy writer; a placeholder like "<pubkey>" or a "0x..." prefix in hand-edited JSON; a key of a different algorithm stored in the pubkey field; whitespace/case issues if from_hex is strict.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- principal has no Ed25519 key for genesis identity
- absent migration source has a digest
- decode migration ledger
- durable capsule disappeared after publish
- durable capsule failed authoritative verification
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/e32d1a9a4c3a583d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/principal_home_migration/unbound.rs:222
Ok(true)
}
fn genesis_public_key_bytes(home: &AstridHome, alias: &PrincipalId) -> io::Result<[u8; 32]> {
let profile =
PrincipalProfile::load_required(home, alias).map_err(|error| profile_io(&error))?;
let device = profile
.auth
.public_keys
.iter()
.min_by_key(|device| (device.created_at, device.key_id.as_str()))
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("principal {alias} has no Ed25519 key for genesis identity"),
)
})?;
let public_key = astrid_crypto::PublicKey::from_hex(&device.pubkey).map_err(|error| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("principal {alias} has an invalid genesis public key: {error}"),
)
})?;
Ok(public_key.into())
}
fn quarantine_entry(
home: &AstridHome,
source: &Path,
file_name: &OsStr,
reason: &str,
) -> io::Result<()> {
let quarantine_root = home.migrations_dir().join(QUARANTINE_DIR);
astrid_core::platform_fs::ensure_private_directory(&quarantine_root)?;
let destination = unique_quarantine_path(&quarantine_root, file_name)?;
let source_parent = source.parent().map(Path::to_path_buf);
fs::rename(source, &destination).map_err(|error| {View on GitHub (pinned to affd8760f4)