{"record":{"id":"c958d3c013b8751c","repo":"astrid-runtime/astrid","slug":"cannot-reload-capsule-id-for-retiring-principa","errorCode":null,"errorMessage":"cannot reload capsule '{id}' for retiring principal '{principal}'","messagePattern":"cannot reload capsule '(.+?)' for retiring principal '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":3092,"sourceCode":"    ///\n    /// If the capsule is already registered, [`Self::restart_capsule`] re-reads\n    /// its source directory — picking up the new content-addressed bytes a\n    /// reinstall wrote (a live upgrade / hot-swap). If it isn't registered yet,\n    /// the currently-installed set is discovered and loaded (a fresh add;\n    /// already-loaded capsules are skipped by `load_capsule`'s guard). Either\n    /// way `astrid.v1.capsules_loaded` is published so the tool surface\n    /// refreshes. Backs [`astrid_core::kernel_api::KernelRequest::ReloadCapsule`].\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub(crate) async fn reload_one_capsule(\n        &self,\n        id: &astrid_capsule_types::CapsuleId,\n        principal: &PrincipalId,\n    ) -> Result<(), anyhow::Error> {\n        let view_guard = self.lock_capsule_view(principal, id).await;\n        let registered = { self.capsules.read().await.get_for(principal, id).is_some() };\n        if registered {\n            if self.capabilities.is_principal_retiring(principal).await {\n                anyhow::bail!(\"cannot reload capsule '{id}' for retiring principal '{principal}'\");\n            }\n            self.restart_capsule(id, principal, None).await?;\n            self.publish_capsules_loaded().await;\n        } else {\n            drop(view_guard);\n            // Build or refresh this principal's view from its installed set.\n            self.ensure_principal_loaded(principal).await;\n            if self.capsules.read().await.get_for(principal, id).is_none()\n                && let Some((_, dir)) = self\n                    .sorted_principal_capsules(principal)\n                    .into_iter()\n                    .find(|(manifest, _)| manifest.package.name == id.as_str())\n            {\n                self.load_capsule(dir, principal)\n                    .await\n                    .map_err(|error| anyhow::anyhow!(\"capsule '{id}' failed to load: {error:#}\"))?;\n            }\n            if self.capsules.read().await.get_for(principal, id).is_none() {","sourceCodeStart":3074,"sourceCodeEnd":3110,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L3074-L3110","documentation":"Reloading a capsule that is already registered to a principal is rejected if that principal is currently retiring. A reload would restart a runtime for an identity being torn down, so the kernel bails instead of calling `restart_capsule`.","triggerScenarios":"Calling the reload/restart API for an existing (registered) capsule while `capabilities.is_principal_retiring(principal)` returns true — e.g., a reload request arriving after a retire request was accepted.","commonSituations":"Config-change automation firing a reload while the same principal is being disabled; health-check-based reload loops colliding with operator retirement; retry queues replaying stale reload requests after retirement.","solutions":["Wait for retirement to finish (or cancel it) before reloading; the capsule will not need reloading if it is being torn down.","Guard reload calls with an `is_principal_retiring` check and skip when true.","Drop queued reload requests for principals marked retiring in your scheduler."],"exampleFix":"// before\nkernel.reload_capsule(id, &principal).await?;\n// after\nif !kernel.capabilities().is_principal_retiring(&principal).await {\n    kernel.reload_capsule(id, &principal).await?;\n}","handlingStrategy":"validation","validationCode":"if kernel.capabilities().is_principal_retiring(&principal).await {\n    return Err(anyhow!(\"skip reload: principal retiring\"));\n}\nif kernel.registered_for(&principal, id) {\n    kernel.reload_capsule(id, &principal).await?;\n}","typeGuard":null,"tryCatchPattern":"match kernel.reload_capsule(id, &principal).await {\n    Err(e) if e.to_string().contains(\"retiring principal\") => cancel_pending_reload(id, principal),\n    other => other,\n}","preventionTips":["Gate reload schedulers on principal lifecycle state.","Drop queued reloads when a retire event is observed.","Debounce reload triggers to reduce overlap with retirement."],"tags":["capsule-lifecycle","principal-retirement","reload","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"}