{"record":{"id":"b524b1c9b9437da4","repo":"astrid-runtime/astrid","slug":"legacy-secret-source-reappeared-after-completed-mi","errorCode":null,"errorMessage":"legacy secret source reappeared after completed migration: {path}","messagePattern":"legacy secret source reappeared after completed migration: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/secret.rs","lineNumber":49,"sourceCode":"    let ledger: MigrationLedger = decode_canonical(&bytes, &path)?;\n    let name = format!(\"principal:{uid}:secrets\");\n    let component = ledger\n        .components\n        .iter()\n        .find(|component| component.name == name);\n    Ok(component.is_some_and(|component| !component.source.present))\n}\n\npub(crate) fn ensure_legacy_secret_deletion_allowed(\n    home: &AstridHome,\n    principal: &PrincipalId,\n    uid: PrincipalUid,\n) -> io::Result<()> {\n    let path = home.secrets_dir().join(principal.as_str());\n    match fs::symlink_metadata(&path) {\n        Ok(_) => {\n            if legacy_secret_source_must_be_absent(home, uid)? {\n                return Err(io::Error::new(\n                    io::ErrorKind::WouldBlock,\n                    format!(\n                        \"legacy secret source reappeared after completed migration: {}\",\n                        path.display()\n                    ),\n                ));\n            }\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => (),\n        Err(error) => return Err(error),\n    }\n    Ok(())\n}\n\n#[cfg(test)]\npub(crate) fn record_absent_legacy_secret_for_test(\n    home: &AstridHome,\n    uid: PrincipalUid,","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/secret.rs#L31-L67","documentation":"Raised by `ensure_legacy_secret_deletion_allowed`: the legacy secret directory for a principal still exists (`secrets_dir/<principal>`), and the migration ledger records that this principal's secret source was migrated away (`source.present == false`). After a completed migration the legacy source must not exist, so its reappearance is blocked with WouldBlock to prevent operating on stale, untracked secrets.","triggerScenarios":"Calling `ensure_legacy_secret_deletion_allowed` when `fs::symlink_metadata(secrets_dir/<principal>)` succeeds AND `legacy_secret_source_must_be_absent` returns true — i.e. the ledger says migration completed for `principal:<uid>:secrets` but the legacy directory is back on disk.","commonSituations":"Restoring the legacy secrets directory from a backup after migration; an old kernel version recreating per-principal secret dirs; a copy operation that resurrected the legacy path during maintenance.","solutions":["Inspect and remove the reappeared legacy directory `<secrets_dir>/<principal>` after confirming no needed data lives there (its contents were migrated).","Reconcile the ledger: if the migration actually did not complete, update/redo the import so `source.present` reflects reality.","Check for old-version processes or backup jobs that recreate the legacy path and stop them.","Re-run the deletion/verification flow after cleanup."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let legacy = home.secrets_dir().join(principal.as_str());\nif legacy.symlink_metadata().is_ok() {\n    eprintln!(\"legacy secret dir still exists for {principal}; reconcile ledger before deletion\");\n}","typeGuard":"fn legacy_secret_path_exists(home: &AstridHome, principal: &PrincipalId) -> bool {\n    home.secrets_dir().join(principal.as_str()).symlink_metadata().is_ok()\n}","tryCatchPattern":"match ensure_legacy_secret_deletion_allowed(&home, &principal, uid) {\n    Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {\n        eprintln!(\"legacy secret source reappeared; inspect {} before retry\", e);\n    }\n    other => other?,\n}","preventionTips":["After migration, delete (not archive in place) the legacy per-principal secret directory.","Prevent old-version binaries from running against the migrated home.","Reconcile the ledger whenever legacy paths are found post-migration."],"tags":["migration","secrets","ledger","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}