{"record":{"id":"863da4b5818ce315","repo":"astrid-runtime/astrid","slug":"invalid-capsule-id-e","errorCode":null,"errorMessage":"invalid capsule ID: {e}","messagePattern":"invalid capsule ID: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/lifecycle.rs","lineNumber":217,"sourceCode":"    .context(\"failed to create scoped KV store\")?;\n    let event_bus = external_bus.unwrap_or_else(|| EventBus::with_capacity(128));\n\n    // Reuse the current tokio runtime when there is one (CLI's\n    // `#[tokio::main]`, kernel handler thread). Only build a new one\n    // for standalone/test contexts.\n    let (owned_rt, handle) = if let Ok(handle) = tokio::runtime::Handle::try_current() {\n        (None, handle)\n    } else {\n        let rt = tokio::runtime::Builder::new_multi_thread()\n            .enable_all()\n            .build()\n            .context(\"failed to build tokio runtime for lifecycle\")?;\n        let handle = rt.handle().clone();\n        (Some(rt), handle)\n    };\n\n    let capsule_id_owned = astrid_capsule::capsule::CapsuleId::new(capsule_id.clone())\n        .map_err(|e| anyhow::anyhow!(\"invalid capsule ID: {e}\"))?;\n    let secret_namespace = if let Some(uid) = principal_uid {\n        astrid_storage::env::principal_secret_namespace(uid, &capsule_id)\n    } else {\n        // A no-env preview lifecycle may use an isolated host-only scope. It\n        // is never an authority source and cannot be selected for manifests\n        // that declare environment state (the guard above fails closed).\n        astrid_storage::env::system_secret_namespace(\"lifecycle-ephemeral\")\n    };\n    let secret_scope = astrid_storage::ScopedKvStore::new(Arc::clone(&kv_store), secret_namespace)\n        .context(\"failed to create lifecycle secret control scope\")?;\n    let secret_store = astrid_storage::build_secret_store(\n        &format!(\"{capsule_id}:{target_principal}\"),\n        secret_scope,\n        handle.clone(),\n    );\n    // Lifecycle hooks use the same host-owned typed environment projection as\n    // steady-state invocations.  Loading this snapshot here is what makes\n    // daemon-staged `--var` values visible before an install/upgrade hook","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/lifecycle.rs#L199-L235","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nrun_lifecycle(&config, \"My Capsule!\", Lifecycle::Install)?;\n// after\nlet capsule_id = CapsuleId::new(\"my-capsule\".to_string())?;\nrun_lifecycle(&config, capsule_id.as_str(), Lifecycle::Install)?;","handlingStrategy":"validation","validationCode":"fn ensure_valid_capsule_id(id: &str) -> anyhow::Result<()> {\n    astrid_capsule::capsule::CapsuleId::new(id.to_string())\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"capsule id {id:?} invalid: {e}\"))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["capsule-id","validation","lifecycle"],"backgroundTag":"invalid-identifier-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}