{"record":{"id":"d1ee8daf019ad9f8","repo":"Hmbown/CodeWhale","slug":"plugin-selector-is-enabled-disable-it-first-w","errorCode":null,"errorMessage":"plugin `{selector}` is enabled; disable it first with /plugin disable {selector}","messagePattern":"plugin `(.+?)` is enabled; disable it first with /plugin disable (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/plugins/mutation.rs","lineNumber":214,"sourceCode":"    })\n}\n\nfn uninstall_plugin(\n    selector: &str,\n    registry: &mut PluginRegistry,\n) -> Result<PluginMutationReceipt> {\n    let plugin = registry\n        .get(selector)\n        .with_context(|| format!(\"Plugin bundle `{selector}` was not found\"))?\n        .clone();\n    if plugin.scope != PluginScope::User {\n        bail!(\n            \"refusing to uninstall the {} bundle `{selector}`; remove it from its own root\",\n            plugin.scope.as_str()\n        );\n    }\n    if plugin.enabled {\n        bail!(\"plugin `{selector}` is enabled; disable it first with /plugin disable {selector}\");\n    }\n    let plugins_dir = user_plugins_dir(registry)?;\n    install::uninstall(plugin.name(), &plugins_dir)?;\n    registry\n        .prune_state_entry(selector)\n        .map_err(anyhow::Error::msg)?;\n    Ok(PluginMutationReceipt {\n        name: plugin.name().to_string(),\n        path: None,\n        content_hash: None,\n        installed_content_hash: None,\n        outcome: PluginMutationOutcome::Uninstalled,\n    })\n}\n\nfn blocked(host: String, needs_approval: bool) -> PluginMutationReceipt {\n    PluginMutationReceipt {\n        name: String::new(),","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/plugins/mutation.rs#L196-L232","documentation":"The plugin uninstall mutation refuses to remove a plugin bundle that is currently enabled. Before touching the filesystem (install::uninstall in the user plugins dir) the code checks plugin.enabled and aborts so an in-use plugin cannot be deleted out from under a running session. This is a deliberate two-step lifecycle: disable first, then uninstall.","triggerScenarios":"Calling the plugin uninstall mutation (the /plugin uninstall path in crates/tui/src/plugins/mutation.rs) with a selector whose registry entry has enabled == true. It also only proceeds for PluginScope::User bundles, so builtin/workspace bundles fail earlier with a different message.","commonSituations":"A user tries to remove a plugin they installed earlier without first turning it off; automation scripts that install/upgrade plugins call uninstall directly and forget the disable step; stale registry state still marks a plugin as enabled after a manual edit of plugin state files.","solutions":["Run /plugin disable {selector} first, then retry /plugin uninstall {selector}.","Verify the plugin's current state with /plugin list (or registry.get(selector)) to confirm it shows disabled before uninstalling.","If the registry state looks wrong (plugin shows enabled but is not really loaded), prune or repair the plugin state entry, then retry disable + uninstall.","Script authors: make uninstall a two-command sequence (disable, then uninstall) and tolerate 'already disabled' on the first step."],"exampleFix":"# before\ncodewhale /plugin uninstall my-plugin   # fails: plugin is enabled\n\n# after\ncodewhale /plugin disable my-plugin\ncodewhale /plugin uninstall my-plugin","handlingStrategy":"validation","validationCode":"// Rust: check registry state before uninstalling\nlet plugin = registry.get(selector)\n    .with_context(|| format!(\"Plugin bundle `{selector}` was not found\"))?;\nif plugin.scope != PluginScope::User {\n    anyhow::bail!(\"cannot uninstall non-user bundle; remove it from its own root\");\n}\nif plugin.enabled {\n    disable_plugin(registry, selector)?; // /plugin disable equivalent\n}\nuninstall_plugin(registry, selector)?;","typeGuard":null,"tryCatchPattern":"// Treat as a recoverable state error: match on the message prefix and retry once after disabling\nmatch uninstall_plugin(&registry, selector) {\n    Err(err) if err.to_string().contains(\"is enabled; disable it first\") => {\n        disable_plugin(&registry, selector)?;\n        uninstall_plugin(&registry, selector)?;\n    }\n    other => other?,\n}","preventionTips":["Model plugin removal as an explicit disable-then-uninstall sequence in scripts and UI flows.","Surface plugin.enabled state in any management UI next to the uninstall action.","Treat 'already disabled' as success in automated disable steps so reruns are idempotent."],"tags":["plugin","uninstall","lifecycle","rust","tui"],"backgroundTag":"invalid-operation-state","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}