{"record":{"id":"ea9adb36fa5b5537","repo":"astrid-runtime/astrid","slug":"principal-alias-has-no-ed25519-key-for-genesis-i","errorCode":null,"errorMessage":"principal {alias} has no Ed25519 key for genesis identity","messagePattern":"principal (.+?) has no Ed25519 key for genesis identity","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/unbound.rs","lineNumber":216,"sourceCode":"            i64::try_from(crate::invite::now_epoch()).unwrap_or(0),\n        ));\n    }\n    if !profile.auth.methods.contains(&AuthMethod::Keypair) {\n        profile.auth.methods.push(AuthMethod::Keypair);\n    }\n    Ok(true)\n}\n\nfn genesis_public_key_bytes(home: &AstridHome, alias: &PrincipalId) -> io::Result<[u8; 32]> {\n    let profile =\n        PrincipalProfile::load_required(home, alias).map_err(|error| profile_io(&error))?;\n    let device = profile\n        .auth\n        .public_keys\n        .iter()\n        .min_by_key(|device| (device.created_at, device.key_id.as_str()))\n        .ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"principal {alias} has no Ed25519 key for genesis identity\"),\n            )\n        })?;\n    let public_key = astrid_crypto::PublicKey::from_hex(&device.pubkey).map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"principal {alias} has an invalid genesis public key: {error}\"),\n        )\n    })?;\n    Ok(public_key.into())\n}\n\nfn quarantine_entry(\n    home: &AstridHome,\n    source: &Path,\n    file_name: &OsStr,\n    reason: &str,","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/unbound.rs#L198-L234","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n\"auth\": { \"public_keys\": [] }\n// after\n\"auth\": { \"public_keys\": [ { \"key_id\": \"k1\", \"created_at\": 1700000000, \"pubkey\": \"<hex-ed25519>\" } ] }","handlingStrategy":"validation","validationCode":"fn profile_has_genesis_key(profile: &PrincipalProfile) -> bool {\n    !profile.auth.public_keys.is_empty()\n}","typeGuard":null,"tryCatchPattern":"match mint_valid_leftover(entry) {\n    Err(e) if e.to_string().contains(\"no Ed25519 key\") => {\n        // cannot derive genesis identity: quarantine instead of minting\n        quarantine(entry)?;\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["crypto","migration","empty-data","identity"],"backgroundTag":"empty-required-field","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}