astrid-runtime/astrid · error

component migration ledger is missing

Error message

component migration ledger is missing: {path}

What it means

The component migration ledger file (returned by `ledger_path`) could not be read because it does not exist. `legacy_secret_source_must_be_absent` requires the ledger to decide whether a principal's legacy secret source must be absent; without it, provenance cannot be established, so it fails with InvalidData before any deletion check can proceed.

Solutions

  1. Run the layout migration / initialization flow first so the component migration ledger is written at the expected path.
  2. Check that `AstridHome` points at the correct home directory (wrong HOME/ASTRID_HOME env var is a common cause).
  3. If the ledger was accidentally deleted, restore it from backup or re-run the import to regenerate it.
  4. Verify the ledger path from `ledger_path(home)` exists and is readable before calling principal-deletion APIs.

Example fix

// before: deleting a principal on a home never migrated
kernel.ensure_legacy_secret_deletion_allowed(&home, &principal, uid)?;
// after: complete migration first
migrate_layout_to_v2(&home)?; // writes the component migration ledger
kernel.ensure_legacy_secret_deletion_allowed(&home, &principal, uid)?;
Defensive patterns

Strategy: validation

Validate before calling

let ledger = ledger_path(&home);
if !ledger.exists() {
    return Err(format!("run layout migration first; missing ledger at {}", ledger.display()));
}
ensure_legacy_secret_deletion_allowed(&home, &principal, uid)?;

Type guard

fn ledger_present(home: &AstridHome) -> bool { ledger_path(home).is_file() }

Try / catch

match ensure_legacy_secret_deletion_allowed(&home, &principal, uid) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("ledger is missing") => {
        eprintln!("home not migrated; run the migration flow first");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `ensure_legacy_secret_deletion_allowed` (or `legacy_secret_source_must_be_absent` directly) for a home where `reject_incomplete_layout_v2` passes but the migration ledger file was never created — i.e. the migration that writes the ledger never ran on this home.

Common situations: Pointing the kernel at a fresh or partially initialized AstridHome; deleting or losing the ledger file while keeping the migrated layout; running deletion logic before running the layout migration.

Understand the failure class

Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/secret.rs:26

use astrid_core::principal::PrincipalId;

use super::host_fs::read_bounded_file;
use super::ledger::{MigrationLedger, decode_canonical};
use super::{MAX_BYTES, ledger_path, reject_incomplete_layout_v2};

#[cfg(test)]
use super::ledger::{DestinationProof, MigrationComponent, SourceIdentity, canonical_json};

/// Return whether migration provenance forbids a legacy secret source for a
/// principal that participated in migration.
pub(crate) fn legacy_secret_source_must_be_absent(
    home: &AstridHome,
    uid: PrincipalUid,
) -> io::Result<bool> {
    reject_incomplete_layout_v2(home)?;
    let path = ledger_path(home);
    let bytes = read_bounded_file(&path, MAX_BYTES)?.ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidData,
            format!("component migration ledger is missing: {}", path.display()),
        )
    })?;
    let ledger: MigrationLedger = decode_canonical(&bytes, &path)?;
    let name = format!("principal:{uid}:secrets");
    let component = ledger
        .components
        .iter()
        .find(|component| component.name == name);
    Ok(component.is_some_and(|component| !component.source.present))
}

pub(crate) fn ensure_legacy_secret_deletion_allowed(
    home: &AstridHome,
    principal: &PrincipalId,
    uid: PrincipalUid,
) -> io::Result<()> {

View on GitHub (pinned to affd8760f4)