{"record":{"id":"6cdee261f6f5f9c5","repo":"Hmbown/CodeWhale","slug":"refusing-invalid-base-url-display-base-url","errorCode":null,"errorMessage":"Refusing invalid base URL '{display_base_url}'","messagePattern":"Refusing invalid base URL '(.+?)'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":1079,"sourceCode":"    {\n        return Err(CatalogRefreshError::InvalidResponse);\n    }\n    let mut stream = response.bytes_stream();\n    let mut body = Vec::new();\n    while let Some(chunk) = stream.next().await {\n        let chunk = chunk.map_err(|_| CatalogRefreshError::Network)?;\n        if body.len().saturating_add(chunk.len()) > max_bytes {\n            return Err(CatalogRefreshError::InvalidResponse);\n        }\n        body.extend_from_slice(&chunk);\n    }\n    String::from_utf8(body).map_err(|_| CatalogRefreshError::InvalidResponse)\n}\n\nfn validate_base_url_security(base_url: &str, provider_allows_insecure_http: bool) -> Result<()> {\n    let display_base_url = redact_url_for_display(base_url);\n    let parsed = reqwest::Url::parse(base_url)\n        .map_err(|_| anyhow::anyhow!(\"Refusing invalid base URL '{display_base_url}'\"))?;\n    let loopback = parsed.host_str().is_some_and(|host| {\n        host.eq_ignore_ascii_case(\"localhost\")\n            || host\n                .trim_matches(['[', ']'])\n                .parse::<std::net::IpAddr>()\n                .is_ok_and(|address| address.is_loopback())\n    });\n    if parsed.scheme() == \"https\" || (parsed.scheme() == \"http\" && loopback) {\n        return Ok(());\n    }\n\n    if parsed.scheme() == \"http\" && provider_allows_insecure_http {\n        logging::warn(\n            \"Using insecure HTTP base URL because this provider sets allow_insecure_http = true in config.toml\",\n        );\n        return Ok(());\n    }\n","sourceCodeStart":1061,"sourceCodeEnd":1097,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L1061-L1097","documentation":"validate_base_url_security parses the configured base URL with reqwest::Url::parse and refuses to build a client when the string is not a valid absolute URL. The URL is redacted for display (credentials stripped) before appearing in the message, so the error never leaks API keys. It is thrown at client construction so a malformed endpoint fails fast instead of producing confusing connection errors later.","triggerScenarios":"Calling client construction/validate_base_url_security with a base_url string that reqwest::Url::parse rejects: missing scheme (e.g. \"api.openai.com/v1\" instead of \"https://api.openai.com/v1\"), whitespace, invalid characters, or a bare host with no scheme.","commonSituations":"Typing a base_url in config.toml without the https:// prefix; copying a URL with a trailing space or stray character; environment substitution producing an empty or partial URL; hostnames pasted without scheme when migrating provider configs.","solutions":["Add an explicit scheme to the base URL in config.toml or the environment, e.g. https://api.openai.com/v1","Trim whitespace and re-check the raw string around the configured value for typos or embedded characters","If the value comes from an env var, echo it (masked) to confirm it resolves to a full URL and is non-empty","If the URL is intentionally non-HTTP, confirm it matches what the provider expects (https or http to a loopback address)"],"exampleFix":"// before (config.toml)\nbase_url = \"api.openai.com/v1\"\n\n// after\nbase_url = \"https://api.openai.com/v1\"","handlingStrategy":"validation","validationCode":"fn base_url_is_valid(base_url: &str) -> bool {\n    matches!(reqwest::Url::parse(base_url), Ok(u) if u.has_host() && matches!(u.scheme(), \"https\" | \"http\"))\n}\n\nif !base_url_is_valid(&cfg.base_url) {\n    eprintln!(\"base_url must be an absolute http(s) URL, got: {:?}\", cfg.base_url);\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the scheme (https://) in configured base URLs","Validate base_url at config-load time, before constructing any client","Keep credentials out of the URL; use a separate api_key field so redaction is never needed","Trim environment-substituted values to catch whitespace and empty results"],"tags":["config","url","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}