astrid-runtime/astrid · error · io::Error
WouldBlock
WouldBlock
Error message
cannot delete principal {principal}: legacy native source is not retired: {} What it means
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.
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/mod.rs:557
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),
Err(error) => return Err(error),
};
collect_workspace_targets(&capsules)
}
/// Agent deletion/rollback must not discard a released source that has not
/// crossed the global barrier. This is synchronous by design: the admin
/// delete path already holds its write fence and must make the decision before
/// unlinking identity or purging durable state.
pub(crate) fn ensure_principal_delete_allowed(
home: &AstridHome,
principal: &PrincipalId,
) -> io::Result<()> {
reject_incomplete_layout_v2(home)?;
let principal_home = home.principal_home(principal);
let root = principal_home.root();
if path_exists(root)? && snapshot_path(root)?.entries != 0 {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
format!(
"cannot delete principal {principal}: legacy native source is not retired: {}",
root.display()
),
));
}
let profile = principal_home.config_dir().join("profile.toml");
if path_exists(&profile)? {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
format!(
"cannot delete principal {principal}: legacy profile is not retired: {}",
profile.display()
),
));
}
Ok(())View on GitHub (pinned to affd8760f4)
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.
Example fix
// before kernel.delete_principal(&principal)?; // WouldBlock: legacy home not retired // after migrate_legacy_principal(&home, &principal)?; // importer retires legacy source assert!(snapshot_path(principal_home.root())?.entries == 0); kernel.delete_principal(&principal)?;
Defensive patterns
Strategy: validation
Validate before calling
let root = home.principal_home(principal).root();
if path_exists(root)? && snapshot_path(root)?.entries != 0 {
// run the legacy migration importer before calling delete
} Type guard
fn legacy_source_retired(root: &Path) -> io::Result<bool> {
Ok(!path_exists(root)? || snapshot_path(root)?.entries == 0)
} Try / catch
match kernel.delete_principal(&principal) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => run_legacy_migration_then_retry(),
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- legacy path {text:?} is not a canonical filesystem path: {er
- legacy principal-home source {}: {detail}
- principal-home migration conflict at {}: {detail}
- invalid logical destination {}: {error}
- invalid principal-home migration receipt: {error}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/012ffc3f2c405831.
Report an issue: GitHub.