Hmbown/CodeWhale · error · anyhow::Error
DSH is not connected; nothing to remove
Error message
DSH is not connected; nothing to remove
What it means
Removing the installed DSH bundle requires a 'current' connection record in the DSH receipt document. When the receipt has no current record — meaning DSH was never connected or the connection was already removed — this anyhow::bail! aborts the removal flow before touching the profile or bundle files.
Solutions
- Check connection state first with the DSH status/connect flow before attempting removal
- Only run the remove command after a successful `codewhale dsh connect`
- If you believe DSH is connected, inspect the receipt file at the DshPaths.receipt location and re-run connect to repopulate the current record
Example fix
// before
remove_bundle(paths, detection, runner)?; // bails when not connected
// after
if dsh_is_connected(&paths)? {
remove_bundle(paths, detection, runner)?;
} Defensive patterns
Strategy: validation
Validate before calling
if !std::path::Path::new(&paths.receipt).exists() { eprintln!("DSH not connected; run connect first"); return; } Try / catch
match remove_bundle_result { Err(e) if e.to_string().contains("not connected") => { /* treat as no-op */ }, Err(e) => return Err(e), Ok(removed) => { /* use removed */ } } Prevention
- Track DSH connection state in your tooling before issuing remove
- Never delete the receipt file manually; use the connect/disconnect flow
- Treat double-remove as expected in idempotent cleanup scripts
When it happens
Trigger: Calling the DSH disconnect/remove flow (e.g. `codewhale dsh disconnect`) when `doc.current` is None: DSH was never connected in this workspace, the receipt file is missing (load creates an empty document), or a prior removal already took the current record.
Common situations: Running disconnect twice; deleting or resetting the DSH receipt file while assuming a connection still exists; a fresh checkout or new workspace where setup was never run.
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
- no bundle is installed
- --app must be `web` or `headless`, got
- cannot resolve the current Codewhale route
- dsh binary path is unknown
- DSH is not connected; run
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/35f0e4e0c88b083e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/integrations/dsh/mod.rs:887
"bundle_dir": paths.bundle_dir.display().to_string(),
"patch_sha256": bundle_record.patch_sha256,
"app_bundle": app.package_name(),
}),
);
Ok(bundle_record)
}
/// `dsh plugin --profile codewhale remove codewhale-dsh-bundle`, then delete
/// only the Codewhale-owned bundle files. The DSH profile directory (and the
/// app bundle link dsh recorded there) is DSH-owned and is left in place.
pub(crate) fn remove_bundle(
paths: &DshPaths,
detection: &DshDetection,
runner: &dyn DshRunner,
) -> Result<Vec<PathBuf>> {
let mut doc = DshReceiptDocument::load(&paths.receipt)?;
let Some(mut record) = doc.current.take() else {
anyhow::bail!("DSH is not connected; nothing to remove");
};
let Some(bundle_record) = record.bundle.take() else {
anyhow::bail!("no bundle is installed");
};
let outcome = bundle::remove_from_profile(runner, detection)?;
let removed = bundle::remove_bundle_files(&paths.bundle_dir)?;
let now = now_rfc3339();
record.updated_at = now.clone();
doc.push(DshReceiptEntry {
event: DshReceiptEvent::RemoveBundle,
at: now,
codewhale_version: codewhale_version(),
dsh_version: detection.version.clone(),
dsh_home: detection.dsh_home.clone(),
overlay_sha256: Some(record.overlay_sha256.clone()),
skin_sha256: record.skin_sha256.clone(),
identity_summary: Some(identity_summary(&record.identity)),
permission_mode: Some(record.identity.permission_mode.as_str().to_string()),View on GitHub (pinned to 73e0f67d83)