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
- Re-run with `--profile web` or `--profile headless` (or omit the override to use the default).
- Use the bundle profile name shown in the error only after `codewhale dsh install-bundle`.
- If the error comes from a stored record (no explicit override), run `codewhale dsh connect` again to rewrite the record with a valid profile.
- 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
- Whitelist profile values in CLI arg parsing before reaching launch_spec.
- Only pass profiles sourced from dsh status output, not free user text.
- Reconnect after upgrading Codewhale if a stored profile name changed.
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
- --app must be `web` or `headless`, got
- cannot resolve the current Codewhale route
- dsh binary path is unknown
- DSH is not connected; nothing to remove
- DSH is not connected; run
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)