Hmbown/CodeWhale · error · anyhow::Error

DSH profile must be `web`, `headless`, or

Error message

DSH profile must be `web`, `headless`, or `{}` (bundle), got `{profile}`

What it means

launch_spec validates the DSH profile name before building the launch command. Only the built-in `web` and `headless` profiles plus the dedicated bundle profile (bundle::BUNDLE_PROFILE) are accepted; any other `--profile` override (or a stored record.profile that is not one of these) is rejected with this message.

Solutions

  1. Re-run with `--profile web` or `--profile headless` (or omit the override to use the default).
  2. Use the bundle profile name shown in the error only after `codewhale dsh install-bundle`.
  3. If the error comes from a stored record (no explicit override), run `codewhale dsh connect` again to rewrite the record with a valid profile.
  4. Check `codewhale dsh launch --help` for the accepted profile values.

Example fix

// before
codewhale dsh launch --profile terminal

// after
codewhale dsh launch --profile headless
Defensive patterns

Strategy: validation

Validate before calling

const VALID_PROFILES: &[&str] = &["web", "headless"];
if let Some(p) = profile_override {
    if !VALID_PROFILES.contains(&p) && p != bundle::BUNDLE_PROFILE {
        return Err(format!("unsupported profile {p}"));
    }
}

Type guard

fn is_valid_profile(p: &str) -> bool {
    matches!(p, "web" | "headless") || p == bundle::BUNDLE_PROFILE
}

Try / catch

match launch_spec(&report, profile_override, &[], &ws) {
    Err(e) if e.to_string().starts_with("DSH profile must be") => {
        eprintln!("use --profile web or --profile headless");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling a dsh launch command with `--profile <name>` where <name> is neither `web`, `headless`, nor the bundle profile name; or the connected record's stored profile field holds an unexpected value.

Common situations: Typo in the profile flag (`webs`, `Web`, `terminal`); copying a dsh-native profile name that Codewhale's integration does not whitelist; an old receipt written by a previous Codewhale version with a since-removed profile name.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

            DshIntegrationState::NotInstalled => anyhow::bail!("dsh is not installed"),
            DshIntegrationState::Detected { .. } => {
                anyhow::bail!("DSH is detected but not connected; run `{CLI_COMMAND} connect`")
            }
        }
    }
    let binary = report
        .detection
        .binary
        .clone()
        .ok_or_else(|| anyhow::anyhow!("dsh binary path is unknown"))?;
    let bundle_installed = record.bundle.is_some();
    let profile = profile_override.unwrap_or(if bundle_installed {
        bundle::BUNDLE_PROFILE
    } else {
        record.profile.as_str()
    });
    if !matches!(profile, "web" | "headless") && profile != bundle::BUNDLE_PROFILE {
        anyhow::bail!(
            "DSH profile must be `web`, `headless`, or `{}` (bundle), got `{profile}`",
            bundle::BUNDLE_PROFILE
        );
    }
    if profile == bundle::BUNDLE_PROFILE && !bundle_installed {
        anyhow::bail!(
            "the `{}` profile carries identity only after `{CLI_COMMAND} install-bundle`",
            bundle::BUNDLE_PROFILE
        );
    }
    let mut args = vec!["--profile".to_string(), profile.to_string()];
    if profile != bundle::BUNDLE_PROFILE {
        // The dedicated bundle profile carries the identity itself; the
        // shipped profiles get it through the overlay.
        args.push("--patch".to_string());
        args.push(report.overlay_path.display().to_string());
    }
    args.extend(extra_args.iter().cloned());

View on GitHub (pinned to 73e0f67d83)