{"record":{"id":"e81a4a322e3b0304","repo":"Hmbown/CodeWhale","slug":"browser-url-cannot-be-empty","errorCode":null,"errorMessage":"browser URL cannot be empty","messagePattern":"browser URL cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/utils.rs","lineNumber":616,"sourceCode":"/// - Windows: `cmd /C start \"\"`\n/// - Other: returns an error.\n///\n/// This is the single entry point for URL opening — every call site in\n/// the codebase should use this instead of hardcoding `Command::new(\"open\")`,\n/// `Command::new(\"xdg-open\")`, or `Command::new(\"cmd\")`.\npub fn open_url(url: &str) -> Result<()> {\n    let mut command = browser_open_command(url)?;\n    command\n        .stdout(std::process::Stdio::null())\n        .stderr(std::process::Stdio::null())\n        .spawn()\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"failed to launch browser command: {e}\"))\n}\n\nfn browser_open_command(url: &str) -> Result<Command> {\n    if url.trim().is_empty() {\n        return Err(anyhow::anyhow!(\"browser URL cannot be empty\"));\n    }\n\n    #[cfg(target_os = \"macos\")]\n    {\n        let mut command = Command::new(\"open\");\n        command.arg(url);\n        Ok(command)\n    }\n\n    #[cfg(any(\n        all(target_os = \"linux\", not(target_env = \"ohos\")),\n        target_os = \"netbsd\",\n        target_os = \"freebsd\",\n        target_os = \"openbsd\",\n        target_os = \"dragonfly\"\n    ))]\n    {\n        let mut command = Command::new(\"xdg-open\");","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/utils.rs#L598-L634","documentation":"browser_open_command validates the URL before building the platform-specific Command and rejects empty or whitespace-only strings. This is a guard against spawning an opener with a blank argument, which would either fail downstream or open something unintended.","triggerScenarios":"Calling open_url(\"\") or open_url(\"   \"), or passing a URL variable that was never populated (failed extraction of a link from a response, empty config value).","commonSituations":"A config key for a docs/help URL left empty; code that builds the URL by string formatting where an upstream field was blank; tests passing an empty literal.","solutions":["Ensure the caller supplies a non-empty URL before invoking open_url.","Check the config/source of the URL string — it is likely empty because an upstream value is missing.","Add a default URL or skip the open call when the URL is blank."],"exampleFix":"// before\nopen_url(config.docs_url)?; // docs_url = \"\"\n// after\nif !config.docs_url.trim().is_empty() {\n    open_url(&config.docs_url)?;\n}","handlingStrategy":"validation","validationCode":"if url.trim().is_empty() {\n    return; // skip opening; nothing to show\n}","typeGuard":"fn is_openable_url(url: &str) -> bool {\n    !url.trim().is_empty() && (url.starts_with(\"http://\") || url.starts_with(\"https://\"))\n}","tryCatchPattern":null,"preventionTips":["Never pass config-derived URLs without checking they are non-empty.","Provide default URLs in config so the field is never blank.","Skip open calls entirely when the source field is empty."],"tags":["validation","url","browser","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}