{"record":{"id":"5c395107eb9014cb","repo":"Hmbown/CodeWhale","slug":"custom-provider-base-url-is-invalid-err","errorCode":null,"errorMessage":"custom provider base URL is invalid: {err}","messagePattern":"custom provider base URL is invalid: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config_persistence.rs","lineNumber":715,"sourceCode":"    }\n    if !value\n        .chars()\n        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-'))\n    {\n        bail!(\"custom provider name may only use letters, numbers, '-' and '_'\");\n    }\n    Ok(value.to_string())\n}\n\nfn normalize_custom_provider_base_url(raw: &str) -> anyhow::Result<String> {\n    use anyhow::bail;\n\n    let value = raw.trim().trim_end_matches('/');\n    if value.is_empty() {\n        bail!(\"custom provider base URL is required\");\n    }\n    let parsed = reqwest::Url::parse(value)\n        .map_err(|err| anyhow::anyhow!(\"custom provider base URL is invalid: {err}\"))?;\n    if !matches!(parsed.scheme(), \"http\" | \"https\") || parsed.host_str().is_none() {\n        bail!(\"custom provider base URL must be an http(s) URL with a host\");\n    }\n    Ok(value.to_string())\n}\n\nfn normalize_optional_custom_provider_field(raw: &str) -> Option<String> {\n    let value = raw.trim();\n    (!value.is_empty()).then(|| value.to_string())\n}\n\npub(crate) fn persist_hotbar_bindings(\n    config_path: Option<&Path>,\n    bindings: &[codewhale_config::HotbarBindingToml],\n) -> anyhow::Result<PathBuf> {\n    let path = config_toml_path(config_path)?;\n    mutate_config_document(&path, |doc| {\n        let table = doc.as_table_mut();","sourceCodeStart":697,"sourceCodeEnd":733,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/config_persistence.rs#L697-L733","documentation":"normalize_custom_provider_base_url validates the base URL supplied for a custom provider. A URL that reqwest::Url::parse rejects (bad scheme, missing host, malformed characters) produces this error before persist_custom_provider writes anything.","triggerScenarios":"persist_custom_provider is called with a base URL string that fails reqwest's URL parsing — e.g. 'myserver:8080/v1' (no scheme), 'http//x', or a URL containing control characters.","commonSituations":"Typing an endpoint without http:// or https://; pasting a URL with embedded whitespace or invisible characters; entering 'localhost:11434' without a scheme; trailing slashes are tolerated but a missing host is not.","solutions":["Include an explicit http:// or https:// scheme, e.g. http://localhost:11434/v1.","Remove trailing slashes, spaces, or invisible characters from the URL.","Verify the URL has a host (IP or domain); a bare path or empty value is rejected."],"exampleFix":"// before\nlet url = normalize_custom_provider_base_url(\"localhost:8080/v1\")?;\n// after\nlet url = normalize_custom_provider_base_url(\"http://localhost:8080/v1\")?;","handlingStrategy":"validation","validationCode":"fn valid_custom_base_url(raw: &str) -> bool {\n    let v = raw.trim().trim_end_matches('/');\n    !v.is_empty()\n        && reqwest::Url::parse(v).map(|u| matches!(u.scheme(), \"http\" | \"https\") && u.host_str().is_some()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match normalize_custom_provider_base_url(input) {\n    Err(e) => eprintln!(\"fix base URL (need http(s) + host): {e}\"),\n    Ok(url) => persist_custom_provider(&url, ...)?,\n}","preventionTips":["Always include an explicit http:// or https:// scheme.","Trim trailing slashes and whitespace before submitting.","Test the URL by opening it in a browser or curl before saving."],"tags":["url","validation","custom-provider","config"],"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-23T08:17:48.524Z"}