astrid-runtime/astrid · error
migration ledger contains duplicate component: {}
Error message
migration ledger contains duplicate component: {} What it means
`validate_ledger_shape` inserts each component name into a `BTreeSet` and fails if a name is inserted twice. This error is thrown when the ledger's `components` array contains two or more entries with the identical component name, which would make the migration state ambiguous (which entry's proof is authoritative?).
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs:614
}
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)
}
#[allow(
clippy::too_many_lines,
reason = "all ledger invariants are checked before admission"
)]
pub(super) fn validate_ledger_shape(ledger: &MigrationLedger) -> io::Result<()> {
let mut names = std::collections::BTreeSet::new();
let mut previous = None;
for component in &ledger.components {
validate_component_name(&component.name)?;
if !names.insert(component.name.clone()) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"migration ledger contains duplicate component: {}",
component.name
),
));
}
if previous
.as_ref()
.is_some_and(|previous: &String| previous >= &component.name)
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"migration ledger components are not canonically sorted",
));
}
previous = Some(component.name.clone());
if component.source.present && component.source.digest == "absent" {View on GitHub (pinned to affd8760f4)
Solutions
- De-duplicate the components array so each component name appears exactly once, keeping the entry whose `destination_proof` matches the on-disk receipts.
- Prefer regenerating the ledger via the library (`write_ledger`) over hand-de-duplication, so proofs stay consistent with the filesystem.
- If the duplication came from a Git merge, redo the merge resolving the ledger file by taking one side's component entry, not concatenating both.
- After fixing, re-run the migration resume; the validation will confirm uniqueness.
Example fix
// before
"components": [
{"name": "principal:01H8X:home", "source": {...}, "destination_proof": "A"},
{"name": "principal:01H8X:home", "source": {...}, "destination_proof": "B"}
]
// after
"components": [
{"name": "principal:01H8X:home", "source": {...}, "destination_proof": "A"}
] Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: component names are unique before writing/validating
fn no_duplicate_components(ledger: &MigrationLedger) -> bool {
let mut seen = std::collections::BTreeSet::new();
ledger.components.iter().all(|c| seen.insert(c.name.clone()))
} Prevention
- De-duplicate by component name before appending entries in any external writer
- Resolve ledger merge conflicts by choosing one entry per component, never concatenating
- Keep the ledger append-only via the library so duplicates cannot be introduced
- Validate generated ledgers with a dry-run resume before committing them
When it happens
Trigger: Calling `validate_ledger_shape` (via `write_ledger` or the decode/validate paths like `reject_incomplete_layout_v2`, `retire_post_barrier_sources`, `resume_existing_layout`) on a ledger JSON where the same `name` appears in multiple component entries — typically from a botched manual merge or a duplicated array element.
Common situations: Merging two branches that both appended the same component to the ledger; a copy-paste duplication when hand-editing; a buggy external tool appending components without de-duplicating.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- unknown migration component name: {name}
- migration component has a non-canonical principal UID: {name
- migration ledger components are not canonically sorted
- present migration source has absent digest: {}
- absent migration source has a digest: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ee7712eedc32fa99.
Report an issue: GitHub.