Hmbown/CodeWhale · error · anyhow::Error

bundle already installed; use

Error message

bundle already installed; use `{CLI_COMMAND} update` or `remove-bundle`

What it means

install_bundle installs the bundle exactly once per connection. If the current receipt record already contains a bundle (record.bundle.is_some()), re-running install-bundle would duplicate or corrupt the installed plugin, so it aborts and points at the correct commands: `update` to refresh or `remove-bundle` to uninstall first.

Solutions

  1. Use `codewhale dsh update` to refresh the already-installed bundle.
  2. Or run `codewhale dsh remove-bundle` first, then `codewhale dsh install-bundle` for a clean reinstall.
  3. Check `codewhale dsh status` to see whether the bundle is already present before scripting installs.

Example fix

// before
codewhale dsh install-bundle  # second time
Error: bundle already installed

// after
codewhale dsh remove-bundle
codewhale dsh install-bundle
Defensive patterns

Strategy: validation

Validate before calling

let status = codewhale_dsh_status()?;
if status.record.as_ref().and_then(|r| r.bundle.as_ref()).is_some() {
    eprintln!("bundle already installed; use update or remove-bundle");
    return Ok(()); // treat as no-op in idempotent scripts
}

Type guard

fn bundle_installed(status: &DshStatusReport) -> bool {
    status.record.as_ref().map_or(false, |r| r.bundle.is_some())
}

Try / catch

match install_bundle(&paths, &detection, runner, &availability, app) {
    Err(e) if e.to_string().contains("bundle already installed") => {
        eprintln!("skipping install; already present (use update to refresh)");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `codewhale dsh install-bundle` a second time without an intervening `codewhale dsh remove-bundle`; scripted install-bundle runs on every launch; retrying a partially-successful earlier install that already recorded the bundle.

Common situations: Idempotency mistake in setup scripts; user forgets the bundle is already installed; attempting to change bundle options (skin/ocean) via install-bundle instead of `update`.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/integrations/dsh/mod.rs:787

pub(crate) fn install_bundle(
    paths: &DshPaths,
    detection: &DshDetection,
    runner: &dyn DshRunner,
    availability: &BundleAvailability,
    app: DshAppBundle,
) -> Result<DshBundleRecord> {
    let pnpm_version = match availability {
        BundleAvailability::Available { pnpm_version } => pnpm_version.clone(),
        BundleAvailability::NotAvailable { reason } => {
            anyhow::bail!("DSH plugin path not available: {reason}")
        }
    };
    let mut doc = DshReceiptDocument::load(&paths.receipt)?;
    let Some(mut record) = doc.current.take() else {
        anyhow::bail!("DSH is not connected; run `{CLI_COMMAND} connect` first");
    };
    if record.bundle.is_some() {
        anyhow::bail!("bundle already installed; use `{CLI_COMMAND} update` or `remove-bundle`");
    }
    let overlay_text = std::fs::read_to_string(&paths.overlay)
        .with_context(|| format!("read {}", paths.overlay.display()))?;
    if sha256_hex(overlay_text.as_bytes()) != record.overlay_sha256 {
        anyhow::bail!("overlay is stale; run `{CLI_COMMAND} update` before install-bundle");
    }
    // install-bundle defaults the palette on; `update --skin false` is the
    // off switch. `connect --skin` records the same decision for a later update.
    let skin = true;
    // The ocean scene follows the recorded decision (default on).
    let ocean = record.ocean_enabled;
    let patch_sha = bundle::write_bundle(
        &paths.bundle_dir,
        &codewhale_version(),
        &overlay_text,
        skin,
        ocean,
    )?;

View on GitHub (pinned to 73e0f67d83)