astrid-runtime/astrid · error

capsule ' ' not found in registry

Error message

capsule '{id}' not found in registry

What it means

Thrown during a capsule restart when `registry.get_for(principal, id)` finds no capsule entry for that principal/id pair in the in-memory capsule registry. The restart flow needs the live capsule handle to read its source directory, and without a registered entry it cannot proceed.

Solutions

  1. Verify the capsule id exists for the given principal before restarting (query the registry or list capsules for the principal)
  2. Load/install the capsule first if it is missing from the registry (the kernel's load path registers it)
  3. Check that the correct principal is being passed; the registry is keyed per principal

Example fix

// before
kernel.restart_capsule(&principal, "my-capsule", None).await?; // Err: not in registry
// after
if kernel.capsule_registered(&principal, "my-capsule").await {
    kernel.restart_capsule(&principal, "my-capsule", None).await?;
} else {
    kernel.load_capsule_for(&principal, "my-capsule").await?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

let registered = kernel.capsules()
    .read().await
    .get_for(principal, id)
    .is_some();
if !registered { return Err(anyhow!("capsule {id} not registered for principal")); }

Type guard

fn is_registered(entry: &Option<CapsuleEntry>) -> bool {
    entry.is_some()
}

Try / catch

match kernel.restart_capsule(principal, id, expected).await {
    Err(e) if e.to_string().contains("not found in registry") => {
        // fallback: load/install the capsule, then retry once
    },
    other => other?,
}

Prevention

When it happens

Trigger: Calling the kernel's restart API (restart_capsule-style method at lib.rs:2122) with an `id` that is not registered under the given `principal` — wrong id, wrong principal, or the capsule was previously uninstalled/stopped and removed from the registry.

Common situations: Restarting a capsule after it failed to load at startup (so it never got registered); passing a capsule id that belongs to a different principal (multi-tenant setups); typos or stale ids held by a client after the capsule was unregistered.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:2122

    /// or [`RestartOutcome::OldInstanceLingering`] when another `Arc` still
    /// holds its already-cancelled resources.
    ///
    /// # Errors
    ///
    /// Returns an error if the capsule has no source directory, cannot be
    /// unregistered, or fails to reload.
    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    async fn restart_capsule(
        &self,
        id: &astrid_capsule_types::CapsuleId,
        principal: &PrincipalId,
        expected_runtime: Option<&astrid_capsule::registry::RuntimeId>,
    ) -> Result<RestartOutcome, anyhow::Error> {
        let (source_dir, current_runtime) = {
            let registry = self.capsules.read().await;
            let capsule = registry
                .get_for(principal, id)
                .ok_or_else(|| anyhow::anyhow!("capsule '{id}' not found in registry"))?;
            let runtime_id = registry
                .runtime_id_for(principal, id)
                .ok_or_else(|| anyhow::anyhow!("capsule '{id}' not found in registry"))?;
            if expected_runtime.is_some_and(|expected| expected != &runtime_id) {
                return Ok(RestartOutcome::Superseded);
            }
            let source_dir = capsule
                .source_dir()
                .map(std::path::Path::to_path_buf)
                .ok_or_else(|| anyhow::anyhow!("capsule '{id}' has no source directory"))?;
            (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())

View on GitHub (pinned to affd8760f4)