astrid-runtime/astrid · error

principal migration ledger is missing the ordinary-home comp

Error message

principal migration ledger is missing the ordinary-home component for {alias}/{uid}

What it means

During proof collection, if an ordinary-home receipt file (`principal-home-{uid}.json`) still exists in the migrations directory but the corresponding ledger component for the principal's ordinary home is missing, this error is thrown. The surviving receipt proves the UID participated in migration, so an incomplete ledger would understate what was migrated — the code fails closed rather than resuming with an incomplete record.

Source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs:130

        .get("audit:migrations:legacy-principal-home-v1")
        .await
        .map_err(storage_io)?
        .map_or_else(DestinationProof::absent, |bytes| {
            DestinationProof::from_hashed_bytes(&bytes)
        });
    for (alias, uid) in directory.bindings() {
        let home_component = format!("principal:{uid}:home");
        if !sources.contains_key(&home_component) {
            // The ledger records only principals that existed at cut-over.
            // Principals admitted later are ordinary v2 state and must not be
            // mistaken for missing legacy-source inventory on every restart.
            // A surviving ordinary-home receipt proves the UID did participate
            // in migration, so omitting its ledger component still fails closed.
            let receipt = home
                .migrations_dir()
                .join(format!("principal-home-{uid}.json"));
            if path_exists(&receipt)? {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "principal migration ledger is missing the ordinary-home component for {alias}/{uid}"
                    ),
                ));
            }
            continue;
        }
        proofs.insert(
            home_component,
            destination_file_proof(
                &home
                    .migrations_dir()
                    .join(format!("principal-home-{uid}.json")),
            )?,
        );
        proofs.insert(
            format!("principal:{uid}:profile"),

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check whether the ledger file was truncated or edited; restore it from a trusted backup.
  2. Delete the stale receipt principal-home-{uid}.json only if the UID genuinely never participated in migration, then re-run.
  3. Otherwise re-run the full migration from a consistent state so ledger and receipts are written together.

Example fix

// before: stale receipt with no ledger component
$ ls ~/.migrations/  # principal-home-1000.json
// after: restore the ledger from backup, or remove the stale receipt and re-migrate
$ rm ~/.migrations/principal-home-1000.json && astrid migrate
Defensive patterns

Strategy: validation

Validate before calling

let receipt = migrations_dir.join(format!("principal-home-{uid}.json"));
if receipt.exists() && !ledger_has_component(&ledger, alias, uid) {
    eprintln!("ledger/receipt mismatch for {alias}/{uid}; re-migrate");
}

Try / catch

match resume_existing_layout(...) {
    Err(e) if e.to_string().contains("missing the ordinary-home component") => {
        eprintln!("restore ledger from backup or remove stale receipt, then re-run");
    }
    r => r?,
}

Prevention

When it happens

Trigger: collect_destination_proofs (via resume_existing_layout, initialize_fresh_layout, migrate_legacy_layout, or existing_ledger_allows_principal_admitted_after_cutover) finds principal-home-{uid}.json on disk but no matching ordinary-home ledger component for {alias}/{uid}.

Common situations: The ledger file was partially restored or edited, dropping components while receipt files remained; a crashed/interrupted migration left receipts on disk with an older ledger version; manual cleanup deleted ledger entries but not the receipts directory.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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