{"record":{"id":"012ffc3f2c405831","repo":"astrid-runtime/astrid","slug":"wouldblock","errorCode":"WouldBlock","errorMessage":"cannot delete principal {principal}: legacy native source is not retired: {}","messagePattern":"cannot delete principal (.+?): legacy native source is not retired: (.+?)","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/mod.rs","lineNumber":557,"sourceCode":"        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),\n        Err(error) => return Err(error),\n    };\n    collect_workspace_targets(&capsules)\n}\n\n/// Agent deletion/rollback must not discard a released source that has not\n/// crossed the global barrier.  This is synchronous by design: the admin\n/// delete path already holds its write fence and must make the decision before\n/// unlinking identity or purging durable state.\npub(crate) fn ensure_principal_delete_allowed(\n    home: &AstridHome,\n    principal: &PrincipalId,\n) -> io::Result<()> {\n    reject_incomplete_layout_v2(home)?;\n    let principal_home = home.principal_home(principal);\n    let root = principal_home.root();\n    if path_exists(root)? && snapshot_path(root)?.entries != 0 {\n        return Err(io::Error::new(\n            io::ErrorKind::WouldBlock,\n            format!(\n                \"cannot delete principal {principal}: legacy native source is not retired: {}\",\n                root.display()\n            ),\n        ));\n    }\n    let profile = principal_home.config_dir().join(\"profile.toml\");\n    if path_exists(&profile)? {\n        return Err(io::Error::new(\n            io::ErrorKind::WouldBlock,\n            format!(\n                \"cannot delete principal {principal}: legacy profile is not retired: {}\",\n                profile.display()\n            ),\n        ));\n    }\n    Ok(())","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/mod.rs#L539-L575","documentation":"Thrown by ensure_principal_delete_allowed when a principal's legacy native home directory still exists and is non-empty. The kernel refuses to delete or roll back a principal while its legacy (pre-migration) native source has not been retired, because deleting would discard data that has not crossed the migration barrier. The io::ErrorKind::WouldBlock signals a temporary, resolvable block rather than data corruption.","triggerScenarios":"Calling the admin principal-delete/rollback path (ensure_principal_delete_allowed) while <home>/<principal-home>/root still contains entries; layout-v2 must already be complete (reject_incomplete_layout_v2 passed first).","commonSituations":"Operators running deletion before the legacy-to-v2 migration importer has drained the principal's home; a partial or failed migration left files behind; an old agent version recreated legacy home files.","solutions":["Run the legacy migration/cut-over for this principal so its native source is imported and retired, then retry the delete.","Inspect the path printed in the error; if the files are already migrated and safe to drop, archive and remove them (or retire the now-empty directory).","Verify the agent/kernel version is current so legacy writers no longer recreate the home directory.","If deletion must proceed, ensure allow_empty_cleanup/retire path is enabled so empty legacy dirs are cleaned instead of blocking."],"exampleFix":"// before\nkernel.delete_principal(&principal)?; // WouldBlock: legacy home not retired\n// after\nmigrate_legacy_principal(&home, &principal)?; // importer retires legacy source\nassert!(snapshot_path(principal_home.root())?.entries == 0);\nkernel.delete_principal(&principal)?;","handlingStrategy":"validation","validationCode":"let root = home.principal_home(principal).root();\nif path_exists(root)? && snapshot_path(root)?.entries != 0 {\n    // run the legacy migration importer before calling delete\n}","typeGuard":"fn legacy_source_retired(root: &Path) -> io::Result<bool> {\n    Ok(!path_exists(root)? || snapshot_path(root)?.entries == 0)\n}","tryCatchPattern":"match kernel.delete_principal(&principal) {\n    Err(e) if e.kind() == io::ErrorKind::WouldBlock => run_legacy_migration_then_retry(),\n    other => other?,\n}","preventionTips":["Always complete the legacy cut-over before administrative deletes.","Pin all agents to versions that no longer write legacy home files.","Snapshot legacy dirs before cleanup so deletion never destroys un-imported data."],"tags":["migration","io","rust","state-barrier"],"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-14T05:17:10.506Z"}