{"record":{"id":"7fa18d731bea4f93","repo":"astrid-runtime/astrid","slug":"legacy-secrets-root-reappeared-after-cut-over-se","errorCode":null,"errorMessage":"legacy secrets root reappeared after cut-over: {secrets_root}","messagePattern":"legacy secrets root reappeared after cut-over: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/mod.rs","lineNumber":936,"sourceCode":"        if allow_empty_cleanup {\n            retire_empty_directory(&host_secrets)?;\n        } else {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy host secret source reappeared after cut-over: {}\",\n                    host_secrets.display()\n                ),\n            ));\n        }\n    }\n    if path_exists(&secrets_root)? {\n        let remaining = snapshot_path(&secrets_root)?;\n        if remaining.entries == 0 {\n            if allow_empty_cleanup {\n                retire_empty_directory(&secrets_root)?;\n            } else {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"legacy secrets root reappeared after cut-over: {}\",\n                        secrets_root.display()\n                    ),\n                ));\n            }\n        }\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests;\n","sourceCodeStart":918,"sourceCodeEnd":951,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/mod.rs#L918-L951","documentation":"Thrown at the end of the legacy migration barrier audit when the legacy secrets root directory itself still exists but is empty. After full cut-over the entire legacy secrets root should be gone; an empty remnant is tolerated only when `allow_empty_cleanup` is set, otherwise the audit returns InvalidData rather than deleting user-visible directories behind the caller's back.","triggerScenarios":"Running the migration verification when `home.secrets_dir()` exists with zero entries and `allow_empty_cleanup` is false. Also triggered when leftover principal/host subdirectories were cleaned but the root was left behind by an older tool version.","commonSituations":"Post-migration audit on a home where an older binary created the secrets root and never removed it; backup/restore tooling recreating the empty directory; scripts that `mkdir -p` the secrets path at startup.","solutions":["Delete the empty legacy secrets root directory (`rmdir <secrets_dir>`; rmdir fails safely if not empty).","Re-run the audit with `allow_empty_cleanup` so the empty root is retired automatically.","Remove any startup/config code that recreates the legacy secrets root directory.","If the root is non-empty, complete the remaining secret imports first; a different error names what remains."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let secrets_root = home.secrets_dir();\nif secrets_root.exists()\n    && std::fs::read_dir(&secrets_root)?.next().is_none()\n{\n    std::fs::remove_dir(&secrets_root)?; // fails safely if not empty\n}\nverify_legacy_migration_barrier(&home, allow_empty_cleanup)?;","typeGuard":"fn is_empty_dir(p: &std::path::Path) -> std::io::Result<bool> {\n    Ok(p.is_dir() && std::fs::read_dir(p)?.next().is_none())\n}","tryCatchPattern":"if let Err(e) = audit {\n    if e.kind() == std::io::ErrorKind::InvalidData {\n        eprintln!(\"legacy remnant: {e}; run with allow_empty_cleanup or rmdir the path\");\n    }\n}","preventionTips":["Do not `mkdir -p` the legacy secrets root in startup scripts.","Enable allow_empty_cleanup so empty remnants are retired automatically.","Audit backup-restore jobs for paths they recreate."],"tags":["migration","filesystem","io"],"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-17T15:17:12.973Z"}