astrid-runtime/astrid · error
cannot reload capsule '{id}' for retiring principal '{princi
Error message
cannot reload capsule '{id}' for retiring principal '{principal}' What it means
Reloading a capsule that is already registered to a principal is rejected if that principal is currently retiring. A reload would restart a runtime for an identity being torn down, so the kernel bails instead of calling `restart_capsule`.
Source
Thrown at crates/astrid-kernel/src/lib.rs:3092
///
/// If the capsule is already registered, [`Self::restart_capsule`] re-reads
/// its source directory — picking up the new content-addressed bytes a
/// reinstall wrote (a live upgrade / hot-swap). If it isn't registered yet,
/// the currently-installed set is discovered and loaded (a fresh add;
/// already-loaded capsules are skipped by `load_capsule`'s guard). Either
/// way `astrid.v1.capsules_loaded` is published so the tool surface
/// refreshes. Backs [`astrid_core::kernel_api::KernelRequest::ReloadCapsule`].
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) async fn reload_one_capsule(
&self,
id: &astrid_capsule_types::CapsuleId,
principal: &PrincipalId,
) -> Result<(), anyhow::Error> {
let view_guard = self.lock_capsule_view(principal, id).await;
let registered = { self.capsules.read().await.get_for(principal, id).is_some() };
if registered {
if self.capabilities.is_principal_retiring(principal).await {
anyhow::bail!("cannot reload capsule '{id}' for retiring principal '{principal}'");
}
self.restart_capsule(id, principal, None).await?;
self.publish_capsules_loaded().await;
} else {
drop(view_guard);
// Build or refresh this principal's view from its installed set.
self.ensure_principal_loaded(principal).await;
if self.capsules.read().await.get_for(principal, id).is_none()
&& let Some((_, dir)) = self
.sorted_principal_capsules(principal)
.into_iter()
.find(|(manifest, _)| manifest.package.name == id.as_str())
{
self.load_capsule(dir, principal)
.await
.map_err(|error| anyhow::anyhow!("capsule '{id}' failed to load: {error:#}"))?;
}
if self.capsules.read().await.get_for(principal, id).is_none() {View on GitHub (pinned to affd8760f4)
Solutions
- Wait for retirement to finish (or cancel it) before reloading; the capsule will not need reloading if it is being torn down.
- Guard reload calls with an `is_principal_retiring` check and skip when true.
- Drop queued reload requests for principals marked retiring in your scheduler.
Example fix
// before
kernel.reload_capsule(id, &principal).await?;
// after
if !kernel.capabilities().is_principal_retiring(&principal).await {
kernel.reload_capsule(id, &principal).await?;
} Defensive patterns
Strategy: validation
Validate before calling
if kernel.capabilities().is_principal_retiring(&principal).await {
return Err(anyhow!("skip reload: principal retiring"));
}
if kernel.registered_for(&principal, id) {
kernel.reload_capsule(id, &principal).await?;
} Try / catch
match kernel.reload_capsule(id, &principal).await {
Err(e) if e.to_string().contains("retiring principal") => cancel_pending_reload(id, principal),
other => other,
} Prevention
- Gate reload schedulers on principal lifecycle state.
- Drop queued reloads when a retire event is observed.
- Debounce reload triggers to reduce overlap with retirement.
When it happens
Trigger: Calling the reload/restart API for an existing (registered) capsule while `capabilities.is_principal_retiring(principal)` returns true — e.g., a reload request arriving after a retire request was accepted.
Common situations: Config-change automation firing a reload while the same principal is being disabled; health-check-based reload loops colliding with operator retirement; retry queues replaying stale reload requests after retirement.
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 load capsule '{id}' for retiring principal '{principa
- cannot replace capsule '{id}' for retiring principal '{princ
- 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/c958d3c013b8751c.
Report an issue: GitHub.