astrid-runtime/astrid · error
cannot load capsule '{id}' for retiring principal '{principa
Error message
cannot load capsule '{id}' for retiring principal '{principal}' What it means
During capsule load, the kernel refuses to materialize a capsule for a principal whose identity is currently in a retiring state. Retiring principals are being torn down, so creating new capsule runtimes for them would leak resources or resurrect state that shutdown is trying to remove. The kernel checks `capabilities.is_principal_retiring` right before computing the WASM hash and creating the runtime.
Source
Thrown at crates/astrid-kernel/src/lib.rs:1726
.as_ref()
.map_or_else(|| manifest, |bound| bound.manifest.clone());
let manifest_path = runtime_dir.join("Capsule.toml");
self.verify_workspace_component_paths(&runtime_dir, &manifest)?;
let id = astrid_capsule_types::CapsuleId::from_static(&manifest.package.name);
let _view_guard = self.lock_capsule_view(principal, &id).await;
let _load_guard = self.capsule_load_lock.lock().await;
if let Some(bound) = bound.as_ref() {
self.confirm_published_materialization(
&runtime_dir,
principal,
&manifest,
&bound.snapshot,
)?;
}
if *principal != PrincipalId::default()
&& self.capabilities.is_principal_retiring(principal).await
{
anyhow::bail!("cannot load capsule '{id}' for retiring principal '{principal}'");
}
let wasm_hash = capsule_instance_hash(&manifest, &runtime_dir);
// `capabilities.uplink` alone remains a principal-scoped daemon/host
// grant unless the operator explicitly promotes it. A manifest that
// actually provides an uplink must be operator-approved.
let system_allowed = self.system_capsules.read().await.contains(id.as_str());
let system_runtime =
classify_runtime_residency(&manifest, &id, system_allowed)?.is_system();
if system_runtime && !manifest.mcp_servers.is_empty() {
anyhow::bail!(
"system-resident capsule '{id}' cannot host principal-bearing stdio MCP servers"
);
}
self.verify_workspace_capsule_tree(&runtime_dir)?;
// Mutable runtimes are authority-scoped. A principal always receives a
// fresh runtime for its immutable UID; only an explicitly classified
// SystemResident service may attach another view to one runtime.View on GitHub (pinned to affd8760f4)
Solutions
- Wait for the principal retirement to complete, or cancel it, before retrying the load.
- Load the capsule under the default/principal-less view if the capsule is not principal-specific.
- Check `capabilities.is_principal_retiring(principal)` before issuing the load and skip if true.
- Serialize retirement and load operations in your orchestration so they cannot overlap.
Example fix
// before
kernel.load_capsule(&dir, &principal).await?;
// after
if !capabilities.is_principal_retiring(&principal).await {
kernel.load_capsule(&dir, &principal).await?;
} else {
eprintln!("principal {principal} is retiring; skipping capsule load");
} Defensive patterns
Strategy: validation
Validate before calling
if (await kernel.capabilities().is_principal_retiring(&principal)).await {
return Err(anyhow!("principal {principal} is retiring; load refused"));
} Try / catch
match kernel.load_capsule(&dir, &principal).await {
Err(e) if e.to_string().contains("retiring principal") => defer_load_until_retirement_done(),
other => other?,
} Prevention
- Check retirement status before every principal-scoped load.
- Serialize retire and load operations in your orchestrator.
- Subscribe to retirement events and pause load queues.
When it happens
Trigger: Calling the capsule-load API with a non-default principal (`principal != PrincipalId::default()`) while that principal is mid-retirement in the capabilities service. A concurrent retire request racing with a load call produces this error.
Common situations: An automation script reloads a principal's capsules at the same time an operator disables/retires that principal; a session reattaches capsules after the user was disabled; retry logic re-fires a load after retirement was initiated.
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
- cannot replace capsule '{id}' for retiring principal '{princ
- cannot reload capsule '{id}' for retiring principal '{princi
- capsule {} disappeared during durable contracts scan
- capsule {} disappeared during durable scan
- legacy capsule {id} changed before retirement
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/2dc5dffc00cbbae3.
Report an issue: GitHub.