astrid-runtime/astrid · error

live replacement of stdio MCP capsule

Error message

live replacement of stdio MCP capsule '{id}' is not yet atomic; restart the daemon to activate these process changes

What it means

Live (in-place) replacement cannot atomically swap out stdio MCP server child processes; restarting the daemon is required to apply manifest changes that affect MCP processes. The kernel deliberately refuses rather than performing a partially-applied, non-atomic update.

Solutions

  1. Restart the daemon so the new capsule (with its MCP processes) activates cleanly.
  2. Unload and reload the capsule instead of live replacement.
  3. If no MCP change is intended, revert the `mcp_servers` section before replacing.

Example fix

// before
kernel.prepare_runtime_replacement(id, source_dir).await?; // fails: has mcp_servers
// after
daemon.restart().await?; // applies mcp_servers changes atomically
Defensive patterns

Strategy: validation

Validate before calling

let manifest = astrid_capsule::discovery::load_manifest(&runtime_dir.join("Capsule.toml"))?;
if !manifest.mcp_servers.is_empty() {
    return Err(anyhow!("mcp_servers present; full daemon restart required"));
}

Try / catch

match kernel.replace_runtime(id, &source_dir).await {
    Err(e) if e.to_string().contains("not yet atomic") => daemon.restart().await,
    other => other,
}

Prevention

When it happens

Trigger: Invoking the live replacement API on a capsule whose (bound) manifest still declares one or more `mcp_servers` entries.

Common situations: Hot-reloading a capsule during development after adding or editing MCP server definitions; CI applying manifest updates to a running daemon with MCP servers.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

            );
        }
        let bound = self.capture_bound_materialization(
            source_dir,
            principal,
            &manifest,
            "replacement source",
        )?;
        let runtime_dir = bound.as_ref().map_or_else(
            || source_dir.to_path_buf(),
            |bound| bound.runtime_dir.clone(),
        );
        let manifest = bound
            .as_ref()
            .map_or_else(|| manifest, |bound| bound.manifest.clone());
        let manifest_path = runtime_dir.join("Capsule.toml");
        self.verify_workspace_component_paths(&runtime_dir, &manifest)?;
        if !manifest.mcp_servers.is_empty() {
            anyhow::bail!(
                "live replacement of stdio MCP capsule '{id}' is not yet atomic; restart the daemon to activate these process changes"
            );
        }
        let artifact = capsule_instance_hash(&manifest, &runtime_dir);
        let system_allowed = self.system_capsules.read().await.contains(id.as_str());
        let system_runtime = classify_runtime_residency(&manifest, id, system_allowed)?.is_system();
        if system_runtime && !manifest.mcp_servers.is_empty() {
            anyhow::bail!(
                "system-resident capsule '{id}' cannot host principal-bearing stdio MCP servers"
            );
        }
        // Replacement authority is the authenticated operator classification
        // and installed receipt, never the ancestry of `source_dir`.
        let actual_scope = if system_runtime {
            astrid_capsule::registry::RuntimeScope::SystemResident
        } else {
            astrid_capsule::registry::RuntimeScope::Principal(
                self.principal_directory.uid_for(principal)?,

View on GitHub (pinned to affd8760f4)