{"record":{"id":"860b4df33a44e391","repo":"Hmbown/CodeWhale","slug":"app-must-be-web-or-headless-got-app","errorCode":null,"errorMessage":"--app must be `web` or `headless`, got `{app}`","messagePattern":"--app must be `web` or `headless`, got `(.+?)`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/integrations/cli.rs","lineNumber":383,"sourceCode":"        }\n        DshIntegrationCommand::Disable => {\n            let paths = DshPaths::from_process()?;\n            let record = dsh::set_disabled(&paths, true)?;\n            println!(\n                \"disabled: overlay kept at {}; launches refused\",\n                record.overlay_path.display()\n            );\n            Ok(())\n        }\n        DshIntegrationCommand::Enable => {\n            let paths = DshPaths::from_process()?;\n            let record = dsh::set_disabled(&paths, false)?;\n            println!(\"enabled: {}\", record.overlay_path.display());\n            Ok(())\n        }\n        DshIntegrationCommand::InstallBundle { app, yes } => {\n            let app = dsh::DshAppBundle::parse(&app)\n                .ok_or_else(|| anyhow::anyhow!(\"--app must be `web` or `headless`, got `{app}`\"))?;\n            let (paths, report) = status_report(config, workspace, false)?;\n            ensure_launchable_dsh(&report)?;\n            let record = report.record.as_ref().ok_or_else(|| {\n                anyhow::anyhow!(\"DSH is not connected; run `{CLI_COMMAND} connect` first\")\n            })?;\n            if let dsh::BundleAvailability::NotAvailable { reason } = &report.bundle_availability {\n                anyhow::bail!(\"DSH plugin path not available: {reason}\");\n            }\n            if matches!(report.state, DshIntegrationState::StaleConfig { .. }) {\n                anyhow::bail!(\"overlay is stale; run `{CLI_COMMAND} update` before install-bundle\");\n            }\n            let app_source = dsh::bundle::app_bundle_source(\n                report\n                    .detection\n                    .binary\n                    .as_ref()\n                    .ok_or_else(|| anyhow::anyhow!(\"dsh binary path is unknown\"))?,\n                app,","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/integrations/cli.rs#L365-L401","documentation":"`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.","triggerScenarios":"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.","commonSituations":"Scripted invocations that compute the `--app` value; users guessing bundle names from the dsh profile list; casing or quoting mistakes in shell wrappers.","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"],"exampleFix":"# before\ncodewhale integrations dsh install-bundle --app \"$DSH_APP\"\n\n// after — validate before invoking, mirroring DshAppBundle::parse\nlet app = if matches!(raw.as_str(), \"web\" | \"headless\") {\n    raw\n} else {\n    anyhow::bail!(\"--app must be `web` or `headless`\");\n};","handlingStrategy":"validation","validationCode":"let app_str = std::env::args().nth(3).unwrap_or_default();\nif !is_valid_dsh_app(&app_str) {\n    eprintln!(\"--app must be `web` or `headless`\");\n    return Ok(());\n}","typeGuard":"fn is_valid_dsh_app(value: &str) -> bool {\n    matches!(value, \"web\" | \"headless\") // mirrors DshAppBundle::parse\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","dsh","cli","validation","arguments"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}