astrid-runtime/astrid · error
decode migration ledger {}: {error}
Error message
decode migration ledger {}: {error} What it means
`decode_canonical` deserializes a migration ledger file from bytes and requires both valid JSON and byte-level canonical form. This error is thrown when `serde_json::from_slice` fails, i.e. the file at the given path is not parseable JSON (syntax error, truncation, binary content, or a type not matching the expected ledger schema).
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs:584
format!("unknown migration component name: {name}"),
));
}
Ok(())
}
pub(super) fn destination_file_proof(path: &Path) -> io::Result<DestinationProof> {
Ok(match read_bounded_file(path, MAX_BYTES)? {
Some(bytes) => DestinationProof::from_hashed_bytes(&bytes),
None => DestinationProof::absent(),
})
}
pub(super) fn decode_canonical<T: for<'de> Deserialize<'de> + Serialize>(
bytes: &[u8],
path: &Path,
) -> io::Result<T> {
let value = serde_json::from_slice(bytes).map_err(|error| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("decode migration ledger {}: {error}", path.display()),
)
})?;
if canonical_json(&value)? != bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("migration ledger is not canonical: {}", path.display()),
));
}
Ok(value)
}
pub(super) fn canonical_json<T: Serialize>(value: &T) -> io::Result<Vec<u8>> {
let mut bytes = serde_json::to_vec(value).map_err(io::Error::other)?;
bytes.push(b'\n');
Ok(bytes)
}View on GitHub (pinned to affd8760f4)
Solutions
- Inspect the file at the path in the message and fix or restore it to valid JSON matching the ledger schema (the serde error text names the exact parse failure).
- Restore the ledger from backup if the content is corrupted beyond repair.
- If a migration was interrupted, re-run the migration from the pre-migration state so a fresh, complete ledger is written.
- Never hand-edit ledgers with non-JSON tools; use the library's `write_ledger` path.
Example fix
// before: truncated ledger
{"components":[{"name":"system:state-db" <- cut off
// after: restore complete file
{"components":[{"name":"system:state-db","source":{...},"destination_proof":"..."}]}
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: file exists, non-empty, and parses as JSON
fn ledger_parses(path: &Path) -> io::Result<()> {
let bytes = std::fs::read(path)?;
if bytes.is_empty() { return Err(io::Error::new(io::ErrorKind::InvalidData, "ledger empty")); }
serde_json::from_slice::<serde_json::Value>(&bytes)
.map(|_| ())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))
} Try / catch
match resume_existing_layout(&home) {
Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().starts_with("decode migration ledger") => {
// ledger file unreadable/corrupt: restore from backup or re-run migration
}
other => other?,
} Prevention
- Write ledger files atomically (temp file + rename) to survive crashes
- Never let editors/formatters save migration ledger files
- Resolve Git merge conflicts in ledgers before running any migration command
- Monitor disk-full conditions during migration runs
When it happens
Trigger: Any read path that calls `decode_canonical` — `reject_incomplete_layout_v2`, `retire_post_barrier_sources`, `resume_existing_layout`, `legacy_secret_source_must_be_absent`, `record_absent_legacy_secret_for_test` — against a ledger/receipt file that is empty, truncated mid-write, corrupted, or not JSON at all.
Common situations: A crash or power loss during ledger write left a partial file; an editor saved the ledger as JSON5/with comments; the file was overwritten by logs or binary data; a Git merge produced conflict markers inside the ledger.
Related errors
- migration ledger is not canonical: {}
- legacy capsule entry has a non-UTF-8 name
- legacy capsule {id} authority verification produced no recei
- legacy capsule {id} authority receipt disappeared
- durable capsule {id} disappeared after publish
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/cd87c280854bc1a1.
Report an issue: GitHub.