{"record":{"id":"2b996d510d5d2f78","repo":"Hmbown/CodeWhale","slug":"bundle-already-installed-use-cli-command-updat","errorCode":null,"errorMessage":"bundle already installed; use `{CLI_COMMAND} update` or `remove-bundle`","messagePattern":"bundle already installed; use `(.+?) update` or `remove-bundle`","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/integrations/dsh/mod.rs","lineNumber":787,"sourceCode":"pub(crate) fn install_bundle(\n    paths: &DshPaths,\n    detection: &DshDetection,\n    runner: &dyn DshRunner,\n    availability: &BundleAvailability,\n    app: DshAppBundle,\n) -> Result<DshBundleRecord> {\n    let pnpm_version = match availability {\n        BundleAvailability::Available { pnpm_version } => pnpm_version.clone(),\n        BundleAvailability::NotAvailable { reason } => {\n            anyhow::bail!(\"DSH plugin path not available: {reason}\")\n        }\n    };\n    let mut doc = DshReceiptDocument::load(&paths.receipt)?;\n    let Some(mut record) = doc.current.take() else {\n        anyhow::bail!(\"DSH is not connected; run `{CLI_COMMAND} connect` first\");\n    };\n    if record.bundle.is_some() {\n        anyhow::bail!(\"bundle already installed; use `{CLI_COMMAND} update` or `remove-bundle`\");\n    }\n    let overlay_text = std::fs::read_to_string(&paths.overlay)\n        .with_context(|| format!(\"read {}\", paths.overlay.display()))?;\n    if sha256_hex(overlay_text.as_bytes()) != record.overlay_sha256 {\n        anyhow::bail!(\"overlay is stale; run `{CLI_COMMAND} update` before install-bundle\");\n    }\n    // install-bundle defaults the palette on; `update --skin false` is the\n    // off switch. `connect --skin` records the same decision for a later update.\n    let skin = true;\n    // The ocean scene follows the recorded decision (default on).\n    let ocean = record.ocean_enabled;\n    let patch_sha = bundle::write_bundle(\n        &paths.bundle_dir,\n        &codewhale_version(),\n        &overlay_text,\n        skin,\n        ocean,\n    )?;","sourceCodeStart":769,"sourceCodeEnd":805,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/integrations/dsh/mod.rs#L769-L805","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`.","solutions":["Use `codewhale dsh update` to refresh the already-installed bundle.","Or run `codewhale dsh remove-bundle` first, then `codewhale dsh install-bundle` for a clean reinstall.","Check `codewhale dsh status` to see whether the bundle is already present before scripting installs."],"exampleFix":"// before\ncodewhale dsh install-bundle  # second time\nError: bundle already installed\n\n// after\ncodewhale dsh remove-bundle\ncodewhale dsh install-bundle","handlingStrategy":"validation","validationCode":"let status = codewhale_dsh_status()?;\nif status.record.as_ref().and_then(|r| r.bundle.as_ref()).is_some() {\n    eprintln!(\"bundle already installed; use update or remove-bundle\");\n    return Ok(()); // treat as no-op in idempotent scripts\n}","typeGuard":"fn bundle_installed(status: &DshStatusReport) -> bool {\n    status.record.as_ref().map_or(false, |r| r.bundle.is_some())\n}","tryCatchPattern":"match install_bundle(&paths, &detection, runner, &availability, app) {\n    Err(e) if e.to_string().contains(\"bundle already installed\") => {\n        eprintln!(\"skipping install; already present (use update to refresh)\");\n    }\n    other => other?,\n}","preventionTips":["Make install-bundle idempotent in scripts by checking status first.","Use `update` for refreshes and option changes, not repeated installs.","Pair every reinstall with an explicit remove-bundle."],"tags":["dsh","bundle","duplicate-operation","state-conflict"],"backgroundTag":"invalid-state-transition","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"}