astrid-runtime/astrid · error
cannot replace capsule '{id}' for retiring principal '{princ
Error message
cannot replace capsule '{id}' for retiring principal '{principal}' What it means
During live replacement, after the replacement has been prepared but before it is swapped in, the kernel re-checks principal retirement. If the principal began retiring while the replacement was being prepared, the freshly built replacement is unloaded/cancelled and the operation is rejected, preventing new runtimes from outliving a shutting-down principal.
Source
Thrown at crates/astrid-kernel/src/lib.rs:2151
(source_dir, runtime_id)
};
// Prepare and prove a route-gated replacement while the current
// generation remains visible and healthy. A preparation or readiness
// failure leaves the running view untouched.
let mut prepared = self
.prepare_runtime_replacement(id, &source_dir, principal, current_runtime.key().scope())
.await?;
let load_guard = self.capsule_load_lock.lock().await;
if self.capabilities.is_principal_retiring(principal).await {
drop(load_guard);
prepared.capsule.retire();
prepared.capsule.request_cancel();
if let Err(cleanup) = prepared.capsule.unload().await {
tracing::warn!(capsule_id = %id, %cleanup, "Failed to unload replacement rejected by principal retirement");
}
anyhow::bail!("cannot replace capsule '{id}' for retiring principal '{principal}'");
}
let (mut previous, replacement) = {
let mut registry = self.capsules.write().await;
if registry.runtime_id_for(principal, id).as_ref() != Some(¤t_runtime) {
drop(registry);
drop(load_guard);
prepared.capsule.request_cancel();
prepared.capsule.unload().await?;
return Ok(RestartOutcome::Superseded);
}
if prepared.system_runtime
&& let Err(error) = registry.validate_system_runtime_replacement(
¤t_runtime,
prepared.capsule.as_ref(),
&prepared.runtime_id,
)
{
drop(registry);View on GitHub (pinned to affd8760f4)
Solutions
- Complete or cancel the principal's retirement, then retry the replacement.
- Check `capabilities.is_principal_retiring(principal)` before starting a replacement and abort early.
- Perform replacements under the default principal when the capsule is not principal-specific.
Example fix
// before
kernel.replace_capsule(id, &principal, source_dir).await?;
// after
if capabilities.is_principal_retiring(&principal).await {
eprintln!("principal retiring; deferring replacement");
} else {
kernel.replace_capsule(id, &principal, source_dir).await?;
} Defensive patterns
Strategy: validation
Validate before calling
if kernel.capabilities().is_principal_retiring(&principal).await {
return Err(anyhow!("defer replacement: principal retiring"));
} Try / catch
match kernel.replace_capsule(id, &principal, &source_dir).await {
Err(e) if e.to_string().contains("retiring principal") => schedule_after_retirement(id, principal),
other => other,
} Prevention
- Re-check retirement immediately before swap-in, and after long prepares.
- Quiesce deployment jobs while retirements are in flight.
- Keep replacement prepare steps short to narrow the race window.
When it happens
Trigger: Calling the replace API for a capsule owned by a principal that enters a retiring state between replacement preparation and the final swap (the retirement check races with the long-running prepare step).
Common situations: A slow build/prepare during which an operator disables the principal; automated retirement triggered concurrently with a deployment-triggered hot replace.
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 reload capsule '{id}' for retiring principal '{princi
- capsule source disappeared while preparing replacement: {}
- capsule {} disappeared during durable contracts scan
- capsule {} disappeared during durable scan
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ce3c9ff7ad996161.
Report an issue: GitHub.