{"record":{"id":"2dc5dffc00cbbae3","repo":"astrid-runtime/astrid","slug":"cannot-load-capsule-id-for-retiring-principal","errorCode":null,"errorMessage":"cannot load capsule '{id}' for retiring principal '{principal}'","messagePattern":"cannot load capsule '(.+?)' for retiring principal '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1726,"sourceCode":"            .as_ref()\n            .map_or_else(|| manifest, |bound| bound.manifest.clone());\n        let manifest_path = runtime_dir.join(\"Capsule.toml\");\n        self.verify_workspace_component_paths(&runtime_dir, &manifest)?;\n        let id = astrid_capsule_types::CapsuleId::from_static(&manifest.package.name);\n        let _view_guard = self.lock_capsule_view(principal, &id).await;\n        let _load_guard = self.capsule_load_lock.lock().await;\n        if let Some(bound) = bound.as_ref() {\n            self.confirm_published_materialization(\n                &runtime_dir,\n                principal,\n                &manifest,\n                &bound.snapshot,\n            )?;\n        }\n        if *principal != PrincipalId::default()\n            && self.capabilities.is_principal_retiring(principal).await\n        {\n            anyhow::bail!(\"cannot load capsule '{id}' for retiring principal '{principal}'\");\n        }\n        let wasm_hash = capsule_instance_hash(&manifest, &runtime_dir);\n        // `capabilities.uplink` alone remains a principal-scoped daemon/host\n        // grant unless the operator explicitly promotes it. A manifest that\n        // actually provides an uplink must be operator-approved.\n        let system_allowed = self.system_capsules.read().await.contains(id.as_str());\n        let system_runtime =\n            classify_runtime_residency(&manifest, &id, system_allowed)?.is_system();\n        if system_runtime && !manifest.mcp_servers.is_empty() {\n            anyhow::bail!(\n                \"system-resident capsule '{id}' cannot host principal-bearing stdio MCP servers\"\n            );\n        }\n        self.verify_workspace_capsule_tree(&runtime_dir)?;\n\n        // Mutable runtimes are authority-scoped. A principal always receives a\n        // fresh runtime for its immutable UID; only an explicitly classified\n        // SystemResident service may attach another view to one runtime.","sourceCodeStart":1708,"sourceCodeEnd":1744,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1708-L1744","documentation":"During capsule load, the kernel refuses to materialize a capsule for a principal whose identity is currently in a retiring state. Retiring principals are being torn down, so creating new capsule runtimes for them would leak resources or resurrect state that shutdown is trying to remove. The kernel checks `capabilities.is_principal_retiring` right before computing the WASM hash and creating the runtime.","triggerScenarios":"Calling the capsule-load API with a non-default principal (`principal != PrincipalId::default()`) while that principal is mid-retirement in the capabilities service. A concurrent retire request racing with a load call produces this error.","commonSituations":"An automation script reloads a principal's capsules at the same time an operator disables/retires that principal; a session reattaches capsules after the user was disabled; retry logic re-fires a load after retirement was initiated.","solutions":["Wait for the principal retirement to complete, or cancel it, before retrying the load.","Load the capsule under the default/principal-less view if the capsule is not principal-specific.","Check `capabilities.is_principal_retiring(principal)` before issuing the load and skip if true.","Serialize retirement and load operations in your orchestration so they cannot overlap."],"exampleFix":"// before\nkernel.load_capsule(&dir, &principal).await?;\n// after\nif !capabilities.is_principal_retiring(&principal).await {\n    kernel.load_capsule(&dir, &principal).await?;\n} else {\n    eprintln!(\"principal {principal} is retiring; skipping capsule load\");\n}","handlingStrategy":"validation","validationCode":"if (await kernel.capabilities().is_principal_retiring(&principal)).await {\n    return Err(anyhow!(\"principal {principal} is retiring; load refused\"));\n}","typeGuard":null,"tryCatchPattern":"match kernel.load_capsule(&dir, &principal).await {\n    Err(e) if e.to_string().contains(\"retiring principal\") => defer_load_until_retirement_done(),\n    other => other?,\n}","preventionTips":["Check retirement status before every principal-scoped load.","Serialize retire and load operations in your orchestrator.","Subscribe to retirement events and pause load queues."],"tags":["capsule-lifecycle","principal-retirement","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}