astrid-runtime/astrid · warning

durable capsule package '{id}' disappeared during removal

Error message

durable capsule package '{id}' disappeared during removal

What it means

Thrown in `remove_one_capsule` when `store.capsules().remove` returns `Ok(false)` — the package existed at snapshot time but was gone (or generation-superseded) at delete time. A concurrent administrative writer won the generation race; the kernel restores the just-closed runtime view via `ensure_principal_loaded` and surfaces this conflict instead of silently reporting success.

Source

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

        // 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,
            Err(error) => {
                self.ensure_principal_loaded(principal).await;
                return Err(anyhow::anyhow!(
                    "remove durable capsule package '{id}': {error}"
                ));
            },
        };
        if !removed {
            // A concurrent administrative writer won the generation race. The
            // durable package is still authoritative; restore the just-closed
            // runtime view before surfacing the conflict.
            self.ensure_principal_loaded(principal).await;
            return Err(anyhow::anyhow!(
                "durable capsule package '{id}' disappeared during removal"
            ));
        }
        Ok(true)
    }

    #[cfg(target_family = "wasm")]
    pub(crate) async fn remove_one_capsule(
        &self,
        _id: &astrid_capsule_types::CapsuleId,
        _principal: &PrincipalId,
    ) -> Result<bool, anyhow::Error> {
        Err(anyhow::anyhow!(
            "durable capsule removal is unavailable on portable hosts"
        ))
    }

    /// Remove every capsule view owned by `principal` before that principal's

View on GitHub (pinned to affd8760f4)

Solutions

  1. Treat this as a lost race: re-read the capsule state; if it is genuinely gone, report success to the user (idempotent delete).
  2. Retry the operation only after confirming the package exists again.
  3. Serialize concurrent administrative deletes per capsule/principal to prevent the race.

Example fix

// before: treat false as hard failure
kernel.remove_one_capsule(&id, &principal).await?;
// after: idempotent handling
match kernel.remove_one_capsule(&id, &principal).await {
    Ok(_) => {},
    Err(e) if e.to_string().contains("disappeared during removal") => {
        // someone else already deleted it; treat as success
    },
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn capsule_still_exists(kernel: &Kernel, id: &CapsuleId, p: &PrincipalId) -> bool { kernel.capsule_exists(id, p) }

Try / catch

match kernel.remove_one_capsule(&id, &principal).await {
    Ok(_) => {},
    Err(e) if e.to_string().contains("disappeared during removal") => {
        // lost generation race; verify state and treat as idempotent success
        if !kernel.capsule_exists(&id, &principal) { info!("already deleted"); }
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Two concurrent removals (or a removal racing another writer) on the same capsule package: the first delete commits, the second's conditional remove finds nothing to delete and returns `false`.

Common situations: Double-clicked/duplicated delete requests from an admin UI; two operators deleting the same capsule simultaneously; a retried request arriving after the first already succeeded.

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/74f7d14f0a6d121a. Report an issue: GitHub.