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'sView on GitHub (pinned to affd8760f4)
Solutions
- Treat this as a lost race: re-read the capsule state; if it is genuinely gone, report success to the user (idempotent delete).
- Retry the operation only after confirming the package exists again.
- 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
- Make delete operations idempotent in the caller.
- Use per-capsule locks or a single admin queue to avoid concurrent writers.
- Debounce duplicate delete requests in admin UIs.
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
- corpus input changed while its baseline snapshot was capture
- workspace capsule manifest changed while it was being read:
- remove durable capsule package '{id}': {error}
- an incomplete capsule authority update exists at {}; remove
- capsule {} disappeared during durable contracts scan
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/74f7d14f0a6d121a.
Report an issue: GitHub.