{"record":{"id":"8c95efd70b1920c6","repo":"astrid-runtime/astrid","slug":"legacy-secret-source-reappeared-after-cut-over","errorCode":null,"errorMessage":"legacy secret source reappeared after cut-over: {}","messagePattern":"legacy secret source reappeared after cut-over: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs","lineNumber":121,"sourceCode":"    for entry in entries {\n        if entry.file_name() == \"__host__\" {\n            continue;\n        }\n        let path = entry.path();\n        let snapshot = snapshot_path(&path)?;\n        if snapshot.entries != 0 {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy secret source remains after migration: {}\",\n                    path.display()\n                ),\n            ));\n        }\n        if allow_empty_cleanup {\n            retire_empty_directory(&path)?;\n        } else {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy secret source reappeared after cut-over: {}\",\n                    path.display()\n                ),\n            ));\n        }\n    }\n    Ok(())\n}\n\n/// Collect capsule directories below a workspace portal without following\n/// redirects.  The migration barrier uses this inventory when checking that\n/// no legacy authority receipts remain attached to a workspace capsule.\npub(super) fn collect_workspace_targets(root: &Path) -> io::Result<Vec<PathBuf>> {\n    const MAX_WORKSPACE_TARGETS: usize = 4096;\n    let metadata = match fs::symlink_metadata(root) {\n        Ok(metadata) => metadata,","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs#L103-L139","documentation":"When cut-over is complete, legacy secret source directories must stay empty. If a directory has content again and empty-dir cleanup is not allowed (`allow_empty_cleanup == false`), the library raises `InvalidData` because a legacy secret source reappeared after migration, meaning aliases would shadow live data.","triggerScenarios":"Calling `ensure_legacy_secret_aliases` after cut-over when a previously retired legacy directory contains files again and the `allow_empty_cleanup` path was not taken (i.e. the error branch runs instead of `retire_empty_directory`).","commonSituations":"An application or cron job rewrote secrets into the old legacy location after migration; a restore/sync tool recreated old files; the migration was run twice with an app still writing legacy paths.","solutions":["Find and stop whatever is writing to the legacy path (process, cron, sync client), remove the recreated files, and re-run migration.","Update applications to use the new secret location instead of the legacy root.","Run migration with cleanup allowed only after confirming nothing recreates legacy content."],"exampleFix":"# before: cron writes to old path\n0 * * * * write-secret legacy-secrets/api/token.txt\n# after\n0 * * * * write-secret .astrid/secrets/api/token.txt","handlingStrategy":"try-catch","validationCode":"fn legacy_quiet(legacy_root: &std::path::Path) -> bool {\n    std::fs::read_dir(legacy_root).map(|rd| rd.filter_map(Result::ok)\n        .all(|e| std::fs::read_dir(e.path()).map(|mut d| d.next().is_none()).unwrap_or(true)))\n    .unwrap_or(true)\n}","typeGuard":null,"tryCatchPattern":"match ensure_legacy_secret_aliases(root, ...) {\n    Err(e) if e.to_string().contains(\"reappeared after cut-over\") => {\n        eprintln!(\"something rewrote legacy secrets; stop writers, clean, re-run\");\n        return Err(e.into());\n    }\n    other => other,\n}","preventionTips":["Identify and stop cron jobs/apps still writing to legacy secret paths.","Update all writers to the new secret location before cut-over.","Monitor legacy root for recreation after migration (e.g. inotify watcher)."],"tags":["migration","race-condition","filesystem"],"backgroundTag":"internal-invariant-violation","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"}