{"record":{"id":"37c514146a49ead7","repo":"aaif-goose/goose","slug":"invalid-base-url","errorCode":null,"errorMessage":"Invalid base URL: {}","messagePattern":"Invalid base URL: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/api_client.rs","lineNumber":457,"sourceCode":"        self.request(path).api_post(payload).await\n    }\n\n    pub async fn response_post(&self, path: &str, payload: &Value) -> Result<Response> {\n        self.request(path).response_post(payload).await\n    }\n\n    pub async fn api_get(&self, path: &str) -> Result<ApiResponse> {\n        self.request(path).api_get().await\n    }\n\n    pub async fn response_get(&self, path: &str) -> Result<Response> {\n        self.request(path).response_get().await\n    }\n\n    fn build_url(&self, path: &str) -> Result<url::Url> {\n        use url::Url;\n        let mut base_url =\n            Url::parse(&self.host).map_err(|e| anyhow::anyhow!(\"Invalid base URL: {}\", e))?;\n\n        let base_path = base_url.path();\n        if !base_path.is_empty() && base_path != \"/\" && !base_path.ends_with('/') {\n            base_url.set_path(&format!(\"{}/\", base_path));\n        }\n\n        let mut url = base_url\n            .join(path)\n            .map_err(|e| anyhow::anyhow!(\"Failed to construct URL: {}\", e))?;\n\n        for (key, value) in &self.default_query {\n            url.query_pairs_mut().append_pair(key, value);\n        }\n\n        Ok(url)\n    }\n}\n","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/api_client.rs#L439-L475","documentation":"ApiClient::build_url parses the configured host string with url::Url::parse before joining any request path. If the host is not an absolute URL — no scheme, empty, or containing characters the URL parser rejects (spaces, control chars) — this error fires before any network traffic happens.","triggerScenarios":"Creating an ApiClient (directly or via a provider constructor) whose host/base_url is 'api.example.com/v1' without https://, is an empty string, contains a trailing copy/paste space or newline, or uses an unsupported scheme like 'example:foo'.","commonSituations":"Users set base URLs in provider config/env vars and forget the scheme (plain hostnames are valid in curl but not in Url::parse); env vars pick up quotes or whitespace from shell export lines; YAML config values wrap across lines producing embedded newlines.","solutions":["Prefix the scheme: 'https://api.example.com/v1' not 'api.example.com/v1'","Print the raw value with delimiters (format!('[{}]', host)) to expose invisible whitespace, then trim it","If the URL comes from env/config, fix it at the source (export line or YAML) rather than patching code"],"exampleFix":"# before\nbase_url = \"api.example.com/v1\"        # Invalid base URL: relative URL without a base\n\n# after\nbase_url = \"https://api.example.com/v1\"","handlingStrategy":"validation","validationCode":"fn valid_base_url(host: &str) -> anyhow::Result<url::Url> {\n    url::Url::parse(host.trim())\n        .map_err(|e| anyhow::anyhow!(\"base URL '{host}' invalid: {e} (did you forget https://?)\"))\n}","typeGuard":null,"tryCatchPattern":"// Validate before constructing the provider; on error, re-prompt for the URL\n// rather than surfacing the parse failure mid-session:\nlet base = valid_base_url(&cfg.base_url)?;\nlet client = ApiClient::new(base.to_string(), auth);","preventionTips":["Always write base URLs with an explicit scheme in config files and env vars","Trim and validate URLs at config-load time (fail fast at startup, not on first request)","Add a config schema/lint that rejects hostnames without ://"],"tags":["configuration","url","validation","env-vars"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}