{"record":{"id":"7409792a4a971e1b","repo":"zeroclaw-labs/zeroclaw","slug":"url-cannot-be-empty-740979","errorCode":null,"errorMessage":"URL cannot be empty","messagePattern":"URL cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/browser_open.rs","lineNumber":47,"sourceCode":"    ) -> anyhow::Result<Self> {\n        Ok(Self {\n            security,\n            allowed_domains: domain_guard::normalize_allowed_domains(\n                allowed_domains,\n                \"browser.allowed_domains\",\n            )?,\n            allowed_private_hosts: domain_guard::normalize_allowed_domains(\n                allowed_private_hosts,\n                \"browser.allowed_private_hosts\",\n            )?,\n        })\n    }\n\n    fn validate_url(&self, raw_url: &str) -> anyhow::Result<String> {\n        let url = raw_url.trim();\n\n        if url.is_empty() {\n            anyhow::bail!(\"URL cannot be empty\");\n        }\n\n        if url.chars().any(char::is_whitespace) {\n            anyhow::bail!(\"URL cannot contain whitespace\");\n        }\n\n        if !(url.starts_with(\"https://\") || url.starts_with(\"http://\")) {\n            anyhow::bail!(\"Only http:// or https:// URLs are allowed\");\n        }\n\n        if self.allowed_domains.is_empty() && self.allowed_private_hosts.is_empty() {\n            anyhow::bail!(\n                \"Browser tool is enabled but no allowed_domains are configured. Add [browser].allowed_domains in config.toml\"\n            );\n        }\n\n        let host = extract_host(url)?;\n        let private_host = domain_guard::is_private_or_local_host(&host);","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/browser_open.rs#L29-L65","documentation":"browser_open trims the raw url argument and rejects it if nothing remains. This is the first guard in validate_url; an empty URL cannot be allowlisted or opened, and failing closed here gives a clearer error than a downstream launcher failure.","triggerScenarios":"Calling the browser_open tool with url=\"\", url=\"   \", or any value that is only whitespace after trim (e.g. a variable that was never filled in).","commonSituations":"Template or format! bugs producing an empty string; the model omitting the URL and caller code defaulting to \"\"; whitespace-only input from misparsed upstream configuration.","solutions":["Pass a complete absolute URL (\"https://example.com\")","Make a missing URL a hard error at the call site instead of defaulting to an empty string","Trim and check is_empty() before invoking the tool"],"exampleFix":"// before\n{\"url\": \"\"}\n// after\n{\"url\": \"https://example.com\"}","handlingStrategy":"validation","validationCode":"let url = raw_url.trim();\nif url.is_empty() {\n    return Err(\"URL cannot be empty\".into());\n}","typeGuard":null,"tryCatchPattern":"match open_tool.execute(args).await {\n    Ok(res) if res.success => { /* ... */ }\n    Ok(res) => {\n        if res.error.as_deref().unwrap_or_default().contains(\"URL cannot be empty\") {\n            // fill in the URL at the source; do not retry with \"\"\n        }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Reject empty URLs at the call site with a clear message","Never default missing URL parameters to an empty string"],"tags":["browser-open","url","validation","empty-input"],"backgroundTag":"empty-url","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}