astrid-runtime/astrid · error

capsule ' ' failed to load

Error message

capsule '{id}' failed to load: {error:#}

What it means

Wrapper error raised when on-demand loading of a capsule from its install directory fails (lib.rs:3108). The kernel found a matching manifest for `id` in the principal's sorted install directories, attempted `load_capsule`, and the underlying load error (compilation, manifest validation, I/O) is chained as `{error:#}`. The original cause is preserved, so read the chained message for the real failure.

Solutions

  1. Read the chained `{error:#}` cause to identify the actual load failure and fix that root problem
  2. Validate the capsule's manifest and source in its install directory (fix syntax, missing files, or schema issues)
  3. Reinstall/redeploy the capsule from a known-good package
  4. Check filesystem permissions on the capsule's install directory

Example fix

// Diagnose the wrapped cause instead of treating it as not-found
match kernel.ensure_capsule_loaded(&principal, &id).await {
    Err(e) if e.to_string().contains("failed to load") => {
        // inspect source(&e) for the real compile/manifest error
        tracing::error!("load failure root cause: {:#}", e);
    },
    _ => {},
}
Defensive patterns

Strategy: try-catch

Validate before calling

let loadable = capsule_manifest_parses(&install_dir)?
    && required_source_files_present(&install_dir)?;

Try / catch

match kernel.ensure_capsule_loaded(principal, id).await {
    Err(e) if e.to_string().contains("failed to load") => {
        let root = e.root_cause(); // surface the chained cause to the operator
        tracing::error!("capsule load root cause: {root}");
    },
    other => other?,
}

Prevention

When it happens

Trigger: Lazily resolving a capsule id during an operation (invoke/restart-style path): a directory matching the id exists under the principal's install dirs, `load_capsule(dir, principal)` returns `Err`, and the error is wrapped at lib.rs:3108.

Common situations: Capsule source on disk fails to compile or validate (bad manifest, missing files, syntax errors) after a deploy or manual edit; permission problems reading the install directory; loading a capsule built against an incompatible capsule SDK version.

Related errors


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

Appendix: source

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

        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() {
                return Err(anyhow::anyhow!(
                    "capsule '{id}' was not found in the install directories or failed to load"
                ));
            }
            self.publish_capsules_loaded().await;
        }
        Ok(())
    }

    /// Unload a single capsule by id without a daemon restart.
    ///
    /// Mirrors the unregister half of [`Self::restart_capsule`]: it removes the
    /// capsule from the running registry and explicitly unloads it (there is no
    /// async `Drop`, so we must do it here to avoid leaking MCP subprocesses and
    /// other engine resources), then publishes `astrid.v1.capsules_loaded` so the
    /// tool surface refreshes — the departed capsule self-excludes from the next

View on GitHub (pinned to affd8760f4)