{"record":{"id":"128e24dfa5f5e726","repo":"astrid-runtime/astrid","slug":"principal-home-migration-conflict-at-detail","errorCode":null,"errorMessage":"principal-home migration conflict at {}: {detail}","messagePattern":"principal-home migration conflict at (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/paths.rs","lineNumber":154,"sourceCode":"        _ => false,\n    }\n}\n\npub(super) fn storage_error(error: &FilesystemError) -> io::Error {\n    io::Error::other(format!(\n        \"authoritative home migration storage error: {error}\"\n    ))\n}\n\npub(super) fn invalid_source(path: &Path, detail: &str) -> io::Error {\n    io::Error::new(\n        io::ErrorKind::InvalidData,\n        format!(\"legacy principal-home source {}: {detail}\", path.display()),\n    )\n}\n\npub(super) fn conflict_path(path: &Path, detail: &str) -> io::Error {\n    io::Error::new(\n        io::ErrorKind::AlreadyExists,\n        format!(\n            \"principal-home migration conflict at {}: {detail}\",\n            path.display()\n        ),\n    )\n}\n\npub(super) fn conflict_fs(path: &FilesystemPath, detail: &str) -> io::Error {\n    io::Error::new(\n        io::ErrorKind::AlreadyExists,\n        format!(\n            \"principal-home migration conflict at {}: {detail}\",\n            path.as_str()\n        ),\n    )\n}\n","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/paths.rs#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: two receipts claim the same alias\nmigrations/receipt-1001.json\nmigrations/receipt-2042.json   # duplicate for same alias\n\n// after: remove the stale duplicate, keep the authoritative one\n$ rm migrations/receipt-2042.json\n$ # re-run migrate_legacy_principal_homes","handlingStrategy":"try-catch","validationCode":"fn has_conflicting_receipts(migrations_dir: &std::path::Path, alias: &str) -> std::io::Result<bool> {\n    let mut count = 0usize;\n    for entry in std::fs::read_dir(migrations_dir)? {\n        let name = entry?.file_name().to_string_lossy().into_owned();\n        if name.starts_with(RECEIPT_PREFIX) && name.ends_with(RECEIPT_SUFFIX) {\n            // count receipts whose parsed alias matches; >1 means conflict\n            count += 1;\n        }\n    }\n    Ok(count > 1)\n}","typeGuard":null,"tryCatchPattern":"match migrate_legacy_principal_homes(&home, &fs, &source) {\n    Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {\n        // path shown in message; back it up, remove stale receipts/destinations, retry once\n        eprintln!(\"conflict at reported path; resolve state and retry: {e}\");\n    }\n    other => other?,\n}","preventionTips":["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)."],"tags":["rust","io","conflict","migration"],"backgroundTag":"file-already-exists","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}