astrid-runtime/astrid · error
principal-home migration conflict at {}: {detail}
Error message
principal-home migration conflict at {}: {detail} What it means
conflict_path constructs the 'principal-home migration conflict at {path}: {detail}' error with ErrorKind::AlreadyExists, reported at the level of an on-disk Path. It signals that migration state is inconsistent at that path: e.g. multiple live migration receipts claim the same alias (receipt_uid_for_alias), a destination already exists, or a legacy source that was supposed to be retired is still present during verification. The library fails closed instead of overwriting or duplicating principal data.
Source
Thrown at crates/astrid-kernel/src/principal_home_migration/paths.rs:154
_ => false,
}
}
pub(super) fn storage_error(error: &FilesystemError) -> io::Error {
io::Error::other(format!(
"authoritative home migration storage error: {error}"
))
}
pub(super) fn invalid_source(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::InvalidData,
format!("legacy principal-home source {}: {detail}", path.display()),
)
}
pub(super) fn conflict_path(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"principal-home migration conflict at {}: {detail}",
path.display()
),
)
}
pub(super) fn conflict_fs(path: &FilesystemPath, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"principal-home migration conflict at {}: {detail}",
path.as_str()
),
)
}
View on GitHub (pinned to affd8760f4)
Solutions
- Read the {detail} to identify which conflict fired (duplicate receipts, existing destination, non-retired source).
- If a previous migration partially completed, inspect the receipt files under the migrations dir and remove duplicates/stale receipts for the alias, then re-run.
- If the destination genuinely should be empty, move the existing directory/file at the conflict path out of the way (back it up) before migrating.
- Never reuse an alias/PrincipalId with an old live receipt; retire the old receipt first, then retry.
Example fix
// before: two receipts claim the same alias migrations/receipt-1001.json migrations/receipt-2042.json # duplicate for same alias // after: remove the stale duplicate, keep the authoritative one $ rm migrations/receipt-2042.json $ # re-run migrate_legacy_principal_homes
Defensive patterns
Strategy: try-catch
Validate before calling
fn has_conflicting_receipts(migrations_dir: &std::path::Path, alias: &str) -> std::io::Result<bool> {
let mut count = 0usize;
for entry in std::fs::read_dir(migrations_dir)? {
let name = entry?.file_name().to_string_lossy().into_owned();
if name.starts_with(RECEIPT_PREFIX) && name.ends_with(RECEIPT_SUFFIX) {
// count receipts whose parsed alias matches; >1 means conflict
count += 1;
}
}
Ok(count > 1)
} Try / catch
match migrate_legacy_principal_homes(&home, &fs, &source) {
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
// path shown in message; back it up, remove stale receipts/destinations, retry once
eprintln!("conflict at reported path; resolve state and retry: {e}");
}
other => other?,
} Prevention
- Run each principal's migration exactly once; make runs idempotent or resumable, not repeatable from scratch.
- Ensure one alias maps to at most one live receipt; retire receipts before reusing an alias.
- Back up and clear destination paths under home/ before a fresh migration attempt.
- Avoid concurrent migrations against the same home directory (use a lock).
When it happens
Trigger: migrate_legacy_principal_homes finding two receipt files for one alias; migrate_one_principal or verify_destinations finding an existing destination at a path; verify_migrated_legacy_principal_sources_retired finding a legacy source still present after migration; retire_one_receipted_source / retire_empty_tree encountering unexpected existing state at the path being retired.
Common situations: A previous partially-completed migration left destinations or receipts behind; the same alias was migrated twice (reused PrincipalId after identity retirement without cleaning receipts); manual copies recreated files under the destination home; interrupted runs left tombstones that make verification see non-retired sources.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- WouldBlock
- legacy path {text:?} is not a canonical filesystem path: {er
- legacy principal-home source {}: {detail}
- invalid logical destination {}: {error}
- invalid principal-home migration receipt: {error}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/128e24dfa5f5e726.
Report an issue: GitHub.