{"record":{"id":"c1ccef5b5e1339c3","repo":"gitbutlerapp/gitbutler","slug":"field-must-be-an-http-or-https-url","errorCode":null,"errorMessage":"{field} must be an HTTP or HTTPS URL","messagePattern":"(.+?) must be an HTTP or HTTPS URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-llm/src/config.rs","lineNumber":409,"sourceCode":"}\n\nfn config_value(config: &gix::config::File, key: &str) -> Option<String> {\n    config.string(key).map(|value| value.to_string())\n}\n\nfn required<'a>(value: &'a str, field: &str) -> Result<&'a str> {\n    let value = value.trim();\n    if value.is_empty() {\n        bail!(\"{field} is required\")\n    }\n    Ok(value)\n}\n\nfn validate_url(value: &str, field: &str) -> Result<()> {\n    let value = required(value, field)?;\n    let url = url::Url::parse(value).with_context(|| format!(\"{field} must be a valid URL\"))?;\n    if !matches!(url.scheme(), \"http\" | \"https\") || url.host_str().is_none() {\n        bail!(\"{field} must be an HTTP or HTTPS URL\")\n    }\n    Ok(())\n}\n\nfn validate_ollama_endpoint(value: &str) -> Result<()> {\n    let value = required(value, \"Ollama endpoint\")?;\n    if value.contains(\"://\") {\n        bail!(\"Ollama endpoint must use host:port format\")\n    }\n    let (host, port) = value\n        .rsplit_once(':')\n        .context(\"Ollama endpoint must use host:port format\")?;\n    if host.trim().is_empty() || port.parse::<u16>().is_err() {\n        bail!(\"Ollama endpoint must use host:port format\")\n    }\n    Ok(())\n}\n","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-llm/src/config.rs#L391-L427","documentation":"Raised by validate_url when a URL-shaped config value parses but is not an http/https URL with a host (e.g. ftp://, file://, or a URL without host). The library requires base URLs to be valid HTTP(S) endpoints.","triggerScenarios":"Providing a base URL like \"localhost:8080\" (no scheme), \"ftp://host\", or \"https://\" (no host) to validate or apply_openai_configuration/apply_lmstudio_configuration.","commonSituations":"Copy-pasting an endpoint without the scheme, pasting a socket path instead of a URL, trailing-scheme typos.","solutions":["Prefix the value with http:// or https:// and include a host","Verify with a quick parse: url::Url::parse(value) has scheme http/https and a host","Use the provider's default endpoint if unsure"],"exampleFix":"// before\nendpoint = \"localhost:11434\"\n// after\nendpoint = \"http://localhost:11434\"","handlingStrategy":"validation","validationCode":"// TS\nfunction isHttpUrl(v: string): boolean {\n  try { const u = new URL(v); return u.protocol === 'http:' || u.protocol === 'https:' && !!u.hostname; }\n  catch { return false; }\n}","typeGuard":"const isHttpUrl = (v: unknown): v is string => {\n  if (typeof v !== 'string') return false;\n  try { const u = new URL(v); return (u.protocol === 'http:' || u.protocol === 'https:') && u.hostname !== ''; }\n  catch { return false; }\n};","tryCatchPattern":null,"preventionTips":["Always include the scheme when entering endpoint URLs","Sanity-check pasted URLs in a browser or curl first","Fall back to provider default endpoints when unsure"],"tags":["config","url","validation"],"backgroundTag":"invalid-url","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}