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

  1. Wait for the principal retirement to complete, or cancel it, before retrying the load.
  2. Load the capsule under the default/principal-less view if the capsule is not principal-specific.
  3. Check `capabilities.is_principal_retiring(principal)` before issuing the load and skip if true.
  4. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/2dc5dffc00cbbae3. Report an issue: GitHub.