astrid-runtime/astrid · error

capsule '{}' changed while activation was in progress

Error message

capsule '{}' changed while activation was in progress

What it means

This error means the published capsule snapshot on disk no longer matches the snapshot the kernel captured when materialization/activation began. The library throws it in confirm_published_materialization as a compare-and-swap style consistency check: between reading the snapshot and confirming activation, the durable published state changed, so activating against the stale snapshot would be unsafe. It is deliberately a hard bail to prevent activating a capsule based on outdated materialized state.

Source

Thrown at crates/astrid-kernel/src/capsule_materialization.rs:222

            .map_err(|error| anyhow::anyhow!("materialize durable capsule package: {error:#}"))?;
        let bound_manifest = astrid_capsule::discovery::load_manifest(&target.join("Capsule.toml"))
            .map_err(|error| anyhow::anyhow!(error))?;
        self.verify_published_materialization(target, principal, &bound_manifest, snapshot)?;
        Ok(bound_manifest)
    }

    /// Recheck the immutable publication after taking activation locks.
    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
    pub(crate) fn confirm_published_materialization(
        &self,
        dir: &Path,
        principal: &astrid_core::principal::PrincipalId,
        manifest: &astrid_capsule_types::manifest::CapsuleManifest,
        snapshot: &astrid_storage::CapsulePackageSnapshot,
    ) -> anyhow::Result<()> {
        let current = self.published_capsule_snapshot(principal, manifest)?;
        if current.as_ref() != Some(snapshot) {
            anyhow::bail!(
                "capsule '{}' changed while activation was in progress",
                manifest.package.name
            );
        }
        self.verify_published_materialization(dir, principal, manifest, snapshot)
    }
}

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-read the capsule snapshot (load_capsule / fetch latest) and retry the materialization with the fresh snapshot
  2. Ensure only one activation per capsule runs at a time (serialize load_capsule / prepare_runtime_replacement calls with a lock)
  3. If a mutable tag was republished, pin the capsule to an immutable digest and re-run activation
  4. Check for background registry-sync jobs or other kernels writing to the published capsule location and stop them during deploys

Example fix

// before: reusing a snapshot captured earlier
let snapshot = cached_snapshot.clone();
kernel.confirm_published_materialization(&principal, &manifest, &snapshot).await?;

// after: re-read the current snapshot, retry on concurrent change
let snapshot = kernel.published_capsule_snapshot(&principal, &manifest)
    .await?
    .ok_or_else(|| anyhow::anyhow!("capsule not published"))?;
match kernel.confirm_published_materialization(&principal, &manifest, &snapshot).await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("changed while activation") => {
        // serialize with a lock and retry once with fresh state
        let _guard = activation_lock.lock().await;
        let fresh = kernel.published_capsule_snapshot(&principal, &manifest).await?;
        kernel.confirm_published_materialization(&principal, &manifest, &fresh).await?;
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: retry

Validate before calling

let current = kernel.published_capsule_snapshot(&principal, &manifest).await?;
if current.as_ref() != Some(&snapshot) {
    // refresh snapshot before calling confirm_published_materialization
}

Try / catch

match result {
    Err(e) if e.to_string().contains("changed while activation was in progress") => retry_with_fresh_snapshot().await?,
    other => other?,
}

Prevention

When it happens

Trigger: confirm_published_materialization is called (directly via load_capsule, or via prepare_runtime_replacement); it re-reads published_capsule_snapshot(principal, manifest) and the result differs from the snapshot passed in — e.g. a concurrent republish/overwrite of the same capsule, a registry sync updating the digest, or a second activation racing the first.

Common situations: Two processes or kernels activating the same capsule version concurrently; an operator re-publishing a capsule tag (mutable tag) while a deployment is in flight; a CI pipeline pushing a new digest for the same package mid-deploy; stale cached snapshot reused after the registry was updated.

Related errors


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