Hmbown/CodeWhale · error · anyhow::Error

plugin_kimi_rollback_destination_missing

Error message

plugin_kimi_rollback_destination_missing

What it means

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.

Solutions

  1. Reinstall the plugin so a correct absolute installed_path is recorded, then retry the rollback
  2. Inspect the plugin install receipt/registry entry and fix the malformed installed_path to a real file under your plugins directory
  3. Manually remove the plugin file and its parent directory entry, then run the plugin command again to regenerate state

Example fix

// before: path stored as bare filename
let installed_path = PathBuf::from("kimi");
// after: store/resolve a full absolute install path
let installed_path = plugins_dir.join("kimi").join("plugin.toml");
Defensive patterns

Strategy: validation

Validate before calling

if installed_path.parent().is_none() {
    return Err(anyhow!("plugin install path must be a file inside a plugins dir: {}", installed_path.display()));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/6445b32b93d70a22. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/commands/groups/plugins/mod.rs:595

        other => CommandResult::error(format!("Unexpected install outcome: {other:?}")),
    }
}

fn rollback_hash_mismatch(
    presentation: &mut dyn CommandPresentationContext,
    plugin: &mut dyn CommandPluginContext,
    name: &str,
    installed_path: Option<&Path>,
    expected: &str,
    actual: Option<&str>,
) -> CommandResult {
    let missing_destination = translate(presentation, "plugin_kimi_rollback_destination_missing");
    // File-level rollback removal crosses the boundary through the plugin
    // facet (D1); the host adapter owns the `crate::plugins::install::uninstall`
    // call.
    let rollback = installed_path
        .and_then(Path::parent)
        .ok_or_else(|| anyhow::anyhow!(missing_destination))
        .and_then(|plugins_dir| {
            plugin
                .uninstall_path(name, plugins_dir)
                .map_err(anyhow::Error::msg)
        });
    let actual = actual
        .map(escape_review_text)
        .unwrap_or_else(|| translate(presentation, "plugin_kimi_hash_unavailable"));
    let name = escape_review_text(name);
    let expected = escape_review_text(expected);
    match rollback {
        Ok(()) => CommandResult::error(
            presentation
                .translate(
                    "plugin_kimi_mismatch_removed",
                    &[
                        ("name", &name),
                        ("expected", &expected),

View on GitHub (pinned to 73e0f67d83)