astrid-runtime/astrid · error
invalid capsule ID
Error message
invalid capsule ID: {e} What it means
run_lifecycle_in_scope validates the caller-supplied capsule ID string by constructing a typed astrid_capsule::capsule::CapsuleId before executing any lifecycle hook. If the string does not satisfy the CapsuleId format rules (e.g. empty or containing forbidden characters), the whole lifecycle run fails immediately. This fail-fast check prevents an invalid identifier from being propagated into storage namespaces, secrets, or KV paths.
Solutions
- Print the exact capsule ID string being passed (debug log) and compare against the ID reported by the install/registry listing commands.
- Validate the string locally with CapsuleId::new before calling run_lifecycle to get an early, clear failure.
- Look up the correct canonical ID from the installed capsule metadata (meta.json) instead of using a directory name or label.
- If the ID looks valid but is rejected, upgrade astrid-capsule crates so the producer and validator agree on the ID format.
Example fix
// before
run_lifecycle(&config, "My Capsule!", Lifecycle::Install)?;
// after
let capsule_id = CapsuleId::new("my-capsule".to_string())?;
run_lifecycle(&config, capsule_id.as_str(), Lifecycle::Install)?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_valid_capsule_id(id: &str) -> anyhow::Result<()> {
astrid_capsule::capsule::CapsuleId::new(id.to_string())
.map(|_| ())
.map_err(|e| anyhow::anyhow!("capsule id {id:?} invalid: {e}"))
} Prevention
- Always take capsule IDs from registry listings or meta.json, never from display names or hand-typed input.
- Validate IDs with CapsuleId::new at the CLI/config boundary before invoking lifecycle APIs.
When it happens
Trigger: Calling run_lifecycle, run_lifecycle_for_principal, or run_lifecycle_for_principal_with_storage with a capsule_id string that CapsuleId::new rejects: empty string, whitespace, non-canonical characters, or an ID copied from a display name rather than the canonical ID.
Common situations: Hand-editing config or CLI args and typo-ing the capsule ID; passing a human-readable capsule name where a canonical ID is expected; older capsules installed before an ID-format change being re-run through lifecycle commands.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- a corpus produced no chunks
- a representation record must cover at least one logical…
- absent migration source has a digest
- alice
- Astrid volume is not a regular file
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/863da4b5818ce315.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-capsule-install/src/lifecycle.rs:217
.context("failed to create scoped KV store")?;
let event_bus = external_bus.unwrap_or_else(|| EventBus::with_capacity(128));
// Reuse the current tokio runtime when there is one (CLI's
// `#[tokio::main]`, kernel handler thread). Only build a new one
// for standalone/test contexts.
let (owned_rt, handle) = if let Ok(handle) = tokio::runtime::Handle::try_current() {
(None, handle)
} else {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.context("failed to build tokio runtime for lifecycle")?;
let handle = rt.handle().clone();
(Some(rt), handle)
};
let capsule_id_owned = astrid_capsule::capsule::CapsuleId::new(capsule_id.clone())
.map_err(|e| anyhow::anyhow!("invalid capsule ID: {e}"))?;
let secret_namespace = if let Some(uid) = principal_uid {
astrid_storage::env::principal_secret_namespace(uid, &capsule_id)
} else {
// A no-env preview lifecycle may use an isolated host-only scope. It
// is never an authority source and cannot be selected for manifests
// that declare environment state (the guard above fails closed).
astrid_storage::env::system_secret_namespace("lifecycle-ephemeral")
};
let secret_scope = astrid_storage::ScopedKvStore::new(Arc::clone(&kv_store), secret_namespace)
.context("failed to create lifecycle secret control scope")?;
let secret_store = astrid_storage::build_secret_store(
&format!("{capsule_id}:{target_principal}"),
secret_scope,
handle.clone(),
);
// Lifecycle hooks use the same host-owned typed environment projection as
// steady-state invocations. Loading this snapshot here is what makes
// daemon-staged `--var` values visible before an install/upgrade hookView on GitHub (pinned to affd8760f4)