astrid-runtime/astrid · error
principal has no Ed25519 key for genesis identity
Error message
principal {alias} has no Ed25519 key for genesis identity What it means
Thrown by genesis_public_key_bytes when the legacy principal profile contains no device public keys at all. The function picks the earliest-created device key (by created_at then key_id) to derive the genesis identity for the minted leftover; with an empty public_keys list there is nothing to derive from, so minting cannot proceed for that principal.
Solutions
- Regenerate or restore the profile so it contains at least one device public key with created_at and key_id.
- Skip/quarantine this principal instead of minting it, if the key genuinely cannot be recovered.
- Verify the legacy tool version that created the profile actually wrote public_keys; re-export from the legacy app if needed.
- Do not hand-edit profiles to remove keys before migration.
Example fix
// before
"auth": { "public_keys": [] }
// after
"auth": { "public_keys": [ { "key_id": "k1", "created_at": 1700000000, "pubkey": "<hex-ed25519>" } ] } Defensive patterns
Strategy: validation
Validate before calling
fn profile_has_genesis_key(profile: &PrincipalProfile) -> bool {
!profile.auth.public_keys.is_empty()
} Try / catch
match mint_valid_leftover(entry) {
Err(e) if e.to_string().contains("no Ed25519 key") => {
// cannot derive genesis identity: quarantine instead of minting
quarantine(entry)?;
}
other => other?,
} Prevention
- Sanity-check profiles for a non-empty public_keys list before migration.
- Re-export profiles from the legacy app rather than hand-editing.
- Don't prune device keys before migration completes.
- Treat keyless leftovers as quarantinable, not mintable.
When it happens
Trigger: ensure_profile_with_genesis_key loads a profile whose auth.public_keys iterator yields no elements when min_by_key looks for the earliest device — i.e. the profile parsed fine but has zero registered keys.
Common situations: A legacy profile created by an old version that stored keys elsewhere; a hand-crafted or sanitized profile with the keys array emptied; keys pruned by rotation cleanup before migration; profile JSON edited manually dropping the keys field.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- principal has an invalid genesis public key
- legacy principal has no durable UID
- absent migration source has a digest
- automatic migration of unreleased Windows layout-one homes…
- cannot migrate an Astrid home without a layout-version…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ea9adb36fa5b5537.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/principal_home_migration/unbound.rs:216
i64::try_from(crate::invite::now_epoch()).unwrap_or(0),
));
}
if !profile.auth.methods.contains(&AuthMethod::Keypair) {
profile.auth.methods.push(AuthMethod::Keypair);
}
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,View on GitHub (pinned to affd8760f4)