{"record":{"id":"f7b02b4ed56db47d","repo":"astrid-runtime/astrid","slug":"legacy-component-source-reappeared-during-ordinary","errorCode":null,"errorMessage":"legacy component source reappeared during ordinary retirement: {}","messagePattern":"legacy component source reappeared during ordinary retirement: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs","lineNumber":276,"sourceCode":"        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"legacy retirement root is not a directory: {}\",\n                path.display()\n            ),\n        ));\n    }\n    if active_mountpoint(path)? {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"legacy source is an active mount: {}\", path.display()),\n        ));\n    }\n    let device = device_id(&metadata);\n    for entry in fs::read_dir(path).map_err(io::Error::other)? {\n        let child = entry.map_err(io::Error::other)?.path();\n        if protected.iter().any(|candidate| candidate == &child) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy component source reappeared during ordinary retirement: {}\",\n                    child.display()\n                ),\n            ));\n        }\n        let child_meta = fs::symlink_metadata(&child).map_err(io::Error::other)?;\n        if child_meta.file_type().is_symlink() || (!child_meta.is_file() && !child_meta.is_dir()) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy source contains redirect or special entry: {}\",\n                    child.display()\n                ),\n            ));\n        }\n        if active_mountpoint(&child)? || device_id(&child_meta) != device {","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs#L258-L294","documentation":"While performing ordinary (bottom-up) retirement, retire_tree found a child of the legacy tree that is listed in the `protected` set — a component-owned path that must never be swept (e.g. a component re-created its state directory after the barrier). Deleting it would destroy live component data, so the library aborts with InvalidData.","triggerScenarios":"retire_tree iterates read_dir children and checks `protected.iter().any(|candidate| candidate == &child)`; any child path present in the protected list triggers this immediately. Happens when a component recreates or never released a directory that the ledger expected to be gone, or when the protected list was built against paths that still exist at retirement time.","commonSituations":"A legacy component service restarted mid-migration and recreated its state directory; an incomplete earlier migration left a component-owned directory behind; misconfigured protected paths that overlap ordinary content.","solutions":["Identify the reported child: if a component recreated it, stop that component and confirm it has migrated to the new layout before retrying.","If the child is stale and genuinely legacy, remove it from the protected set (or delete it via the component's supported path) and rerun retirement.","Verify the protected-path list passed to retirement is correct — overly broad protection entries cause false conflicts.","Never force-delete protected paths by hand; route deletion through the owning component or migration tooling."],"exampleFix":"# before\nsystemctl start legacy-component   # recreates its state dir during retirement\n// after\nsystemctl stop legacy-component\nastrid migrate   # protected path no longer reappears; retirement completes","handlingStrategy":"try-catch","validationCode":"// Confirm no protected child exists in the legacy tree before retiring\nfn protected_children(path: &std::path::Path, protected: &[std::path::PathBuf]) -> std::io::Result<Vec<std::path::PathBuf>> {\n    Ok(std::fs::read_dir(path)?\n        .filter_map(|e| e.ok().map(|e| e.path()))\n        .filter(|c| protected.iter().any(|p| p == c))\n        .collect())\n}","typeGuard":null,"tryCatchPattern":"match retire_tree(path, &expected, &protected) {\n    Err(e) if e.to_string().contains(\"reappeared during ordinary retirement\") => {\n        let child = extract_path(&e.to_string());\n        eprintln!(\"stop the owning component and handle {child} via its supported path\");\n    }\n    other => other?,\n}","preventionTips":["Ensure all components have migrated (and their services stopped) before post-barrier retirement","Keep the protected-path list precise; stale or overly broad entries cause false conflicts","Re-run retirement after cleanup — the check is per-entry and idempotent for already-removed trees"],"tags":["filesystem","migration","concurrency","rust"],"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-14T11:17:12.474Z"}