astrid-runtime/astrid · error

authoritative principal store is unavailable

Error message

authoritative principal store is unavailable

What it means

Thrown in `remove_one_capsule` when the kernel's `principal_store` field is `None`. The principal store is the authoritative durable registry of capsule packages; without it the kernel cannot read or delete durable state, so removal is refused instead of operating on a stale or in-memory-only view. This is a startup/initialization invariant: the store should be attached before any capsule administration is attempted.

Source

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

    /// Atomically remove one capsule package from the authenticated owner's
    /// durable registry, then tear down the corresponding live view. Native
    /// install directories are never consulted or deleted by this path.
    ///
    /// # Errors
    ///
    /// Returns an error when the durable store or owner mapping is unavailable,
    /// the registry mutation fails, or the live view cannot be unloaded.
    #[cfg(not(target_family = "wasm"))]
    pub(crate) async fn remove_one_capsule(
        &self,
        id: &astrid_capsule_types::CapsuleId,
        principal: &PrincipalId,
    ) -> Result<bool, anyhow::Error> {
        let store = self
            .principal_store
            .clone()
            .ok_or_else(|| anyhow::anyhow!("authoritative principal store is unavailable"))?;
        let uid = self
            .principal_directory
            .uid_for(principal)
            .map_err(|error| anyhow::anyhow!("resolve durable owner for {principal}: {error}"))?;
        let owner = astrid_storage::StateOwner::Principal(uid);
        let snapshot = store
            .capsules()
            .get_snapshot(&owner, id.as_str())
            .map_err(|error| anyhow::anyhow!("read durable capsule package '{id}': {error}"))?;
        if snapshot.is_none() {
            return Ok(false);
        }
        // Quiesce and unload before deleting the durable package. If unload
        // fails, the package remains authoritative and can be retried on the
        // next request; no live runtime is left without its registry source.
        let _ = self.unload_one_capsule(id, principal).await?;
        let removed = match store.capsules().remove(&owner, id.as_str()) {
            Ok(removed) => removed,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Ensure the kernel is initialized with a durable principal store before accepting capsule removal requests (check the store-attach step for silent failures).
  2. Guard callers: check whether the durable store is configured before invoking `remove_one_capsule`.
  3. If running in an intentionally ephemeral mode, use in-memory unload (`unload_one_capsule`) instead of durable removal.

Example fix

// before: kernel built without store
let kernel = AstridKernel::builder().build()?;
kernel.remove_one_capsule(&id, &principal).await?;
// after
let kernel = AstridKernel::builder()
    .with_principal_store(durable_store)
    .build()?;
kernel.remove_one_capsule(&id, &principal).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn can_remove_durably(kernel: &Kernel) -> bool { kernel.has_principal_store() }

Type guard

fn store_configured(kernel: &Kernel) -> bool { kernel.principal_store().is_some() }

Try / catch

match kernel.remove_one_capsule(&id, &principal).await {
    Ok(removed) => info!("removed={removed}"),
    Err(e) if e.to_string().contains("principal store is unavailable") => {
        warn!("durable store not configured; re-initialize kernel with storage");
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `remove_one_capsule` on a kernel instance constructed without a durable principal store (store attach failed, was skipped in an embedded/test configuration, or the kernel is running in a degraded mode where the store handle was never wired up).

Common situations: Embedding astrid-kernel in a harness or test that builds the kernel without persistent storage; a failed storage backend initialization silently leaving the store `None`; running a native build against a misconfigured storage path so store attachment never completes before an admin delete request arrives.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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