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
- Run the layout migration / initialization flow first so the component migration ledger is written at the expected path.
- Check that `AstridHome` points at the correct home directory (wrong HOME/ASTRID_HOME env var is a common cause).
- If the ledger was accidentally deleted, restore it from backup or re-run the import to regenerate it.
- 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
- Always run the layout migration before any principal-deletion flow.
- Check that HOME/ASTRID_HOME points at the intended home.
- Back up the migration ledger; treat it as required state.
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
- absent migration source has a digest
- destination proof is missing its payload
- destination receipt changed for migration component
- historical principal home receipt is missing or changed
- insufficient free space for layout migration: need
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)