{"record":{"id":"6445b32b93d70a22","repo":"Hmbown/CodeWhale","slug":"plugin-kimi-rollback-destination-missing","errorCode":null,"errorMessage":"plugin_kimi_rollback_destination_missing","messagePattern":"plugin_kimi_rollback_destination_missing","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/commands/groups/plugins/mod.rs","lineNumber":595,"sourceCode":"        other => CommandResult::error(format!(\"Unexpected install outcome: {other:?}\")),\n    }\n}\n\nfn rollback_hash_mismatch(\n    presentation: &mut dyn CommandPresentationContext,\n    plugin: &mut dyn CommandPluginContext,\n    name: &str,\n    installed_path: Option<&Path>,\n    expected: &str,\n    actual: Option<&str>,\n) -> CommandResult {\n    let missing_destination = translate(presentation, \"plugin_kimi_rollback_destination_missing\");\n    // File-level rollback removal crosses the boundary through the plugin\n    // facet (D1); the host adapter owns the `crate::plugins::install::uninstall`\n    // call.\n    let rollback = installed_path\n        .and_then(Path::parent)\n        .ok_or_else(|| anyhow::anyhow!(missing_destination))\n        .and_then(|plugins_dir| {\n            plugin\n                .uninstall_path(name, plugins_dir)\n                .map_err(anyhow::Error::msg)\n        });\n    let actual = actual\n        .map(escape_review_text)\n        .unwrap_or_else(|| translate(presentation, \"plugin_kimi_hash_unavailable\"));\n    let name = escape_review_text(name);\n    let expected = escape_review_text(expected);\n    match rollback {\n        Ok(()) => CommandResult::error(\n            presentation\n                .translate(\n                    \"plugin_kimi_mismatch_removed\",\n                    &[\n                        (\"name\", &name),\n                        (\"expected\", &expected),","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/commands/groups/plugins/mod.rs#L577-L613","documentation":"Thrown during plugin rollback (rollback_hash_mismatch) when the installed plugin path has no parent directory to use as the plugins dir, so the file-level rollback uninstall cannot name a destination. `Path::parent` returns None only for root or empty paths, meaning the recorded install path itself is malformed. The host adapter owns `crate::plugins::install::uninstall`; this guard refuses to guess where the plugin lives.","triggerScenarios":"Calling the plugin rollback path (via render_install_receipt) with an `installed_path` that is empty or a filesystem root, so `installed_path.and_then(Path::parent)` yields None and `plugin.uninstall_path` is never invoked.","commonSituations":"A corrupted or hand-edited plugin registry/install receipt recorded a bare filename or root path instead of a full install path; migrating plugin state across machines with a different install layout.","solutions":["Reinstall the plugin so a correct absolute installed_path is recorded, then retry the rollback","Inspect the plugin install receipt/registry entry and fix the malformed installed_path to a real file under your plugins directory","Manually remove the plugin file and its parent directory entry, then run the plugin command again to regenerate state"],"exampleFix":"// before: path stored as bare filename\nlet installed_path = PathBuf::from(\"kimi\");\n// after: store/resolve a full absolute install path\nlet installed_path = plugins_dir.join(\"kimi\").join(\"plugin.toml\");","handlingStrategy":"validation","validationCode":"if installed_path.parent().is_none() {\n    return Err(anyhow!(\"plugin install path must be a file inside a plugins dir: {}\", installed_path.display()));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always record absolute, file-level install paths (not bare names) in the plugin registry","Validate plugin registry entries after import/migration before allowing rollback operations"],"tags":["plugin","rollback","path","filesystem"],"backgroundTag":"directory-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}