Hmbown/CodeWhale · error
--app must be `web` or `headless`, got `{app}`
Error message
--app must be `web` or `headless`, got `{app}` What it means
`install-bundle` accepts only the two shipped app bundles: `DshAppBundle::parse` returns Some for exactly `web` or `headless` and None for anything else. This is pure argument validation that fires before any filesystem probe, dsh detection, or pnpm check — nothing has been read or written when it triggers.
Source
Thrown at crates/tui/src/integrations/cli.rs:383
}
DshIntegrationCommand::Disable => {
let paths = DshPaths::from_process()?;
let record = dsh::set_disabled(&paths, true)?;
println!(
"disabled: overlay kept at {}; launches refused",
record.overlay_path.display()
);
Ok(())
}
DshIntegrationCommand::Enable => {
let paths = DshPaths::from_process()?;
let record = dsh::set_disabled(&paths, false)?;
println!("enabled: {}", record.overlay_path.display());
Ok(())
}
DshIntegrationCommand::InstallBundle { app, yes } => {
let app = dsh::DshAppBundle::parse(&app)
.ok_or_else(|| anyhow::anyhow!("--app must be `web` or `headless`, got `{app}`"))?;
let (paths, report) = status_report(config, workspace, false)?;
ensure_launchable_dsh(&report)?;
let record = report.record.as_ref().ok_or_else(|| {
anyhow::anyhow!("DSH is not connected; run `{CLI_COMMAND} connect` first")
})?;
if let dsh::BundleAvailability::NotAvailable { reason } = &report.bundle_availability {
anyhow::bail!("DSH plugin path not available: {reason}");
}
if matches!(report.state, DshIntegrationState::StaleConfig { .. }) {
anyhow::bail!("overlay is stale; run `{CLI_COMMAND} update` before install-bundle");
}
let app_source = dsh::bundle::app_bundle_source(
report
.detection
.binary
.as_ref()
.ok_or_else(|| anyhow::anyhow!("dsh binary path is unknown"))?,
app,View on GitHub (pinned to 8880682c63)
Solutions
- Use exactly `--app web` or `--app headless`
- Check the accepted values in the command help before scripting the flag
- Keep the value lowercase — the parse is exact-match and case-sensitive
- If the value comes from a variable, default it explicitly (`${APP:-web}`) instead of passing an empty string
Example fix
# before
codewhale integrations dsh install-bundle --app "$DSH_APP"
// after — validate before invoking, mirroring DshAppBundle::parse
let app = if matches!(raw.as_str(), "web" | "headless") {
raw
} else {
anyhow::bail!("--app must be `web` or `headless`");
}; Defensive patterns
Strategy: validation
Validate before calling
let app_str = std::env::args().nth(3).unwrap_or_default();
if !is_valid_dsh_app(&app_str) {
eprintln!("--app must be `web` or `headless`");
return Ok(());
} Type guard
fn is_valid_dsh_app(value: &str) -> bool {
matches!(value, "web" | "headless") // mirrors DshAppBundle::parse
} Prevention
- Drive the flag from an enum/clap derive instead of a free string so invalid values cannot reach the CLI
- Default --app explicitly in scripts rather than passing possibly-empty variables
- Check command help before scripting new flags
When it happens
Trigger: Passing `codewhale integrations dsh install-bundle --app <value>` where value is not exactly `web` or `headless`: e.g. `--app web-ui`, `--app Web` (the parse is case-sensitive), `--app bundle`, or an empty string from an unset shell variable.
Common situations: Scripted invocations that compute the `--app` value; users guessing bundle names from the dsh profile list; casing or quoting mistakes in shell wrappers.
Related errors
- unknown argument: ${arg}
- unknown argument: ${arg}
- --days must be an integer from 1 through 90
- unknown argument: ${arg}
- duration {s:?} resolved to zero seconds
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/860b4df33a44e391.
Report an issue: GitHub.