{"record":{"id":"89077585981d47c6","repo":"JuliusBrussee/caveman","slug":"provider-q-upstream-url-must-be-an-absolute-url-w","errorCode":null,"errorMessage":"provider %q upstream URL must be an absolute URL with a host","messagePattern":"provider %q upstream URL must be an absolute URL with a host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":523,"sourceCode":"\t\t\treturn fmt.Errorf(\"ambiguous escaped path sequence %s\", escape)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc parseBaseURL(raw, provider string) (*url.URL, error) {\n\tif strings.TrimSpace(raw) == \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q has no configured upstream URL\", provider)\n\t}\n\tu, err := url.Parse(strings.TrimSpace(raw))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL scheme %q is not allowed\", provider, u.Scheme)\n\t}\n\tif !u.IsAbs() || u.Host == \"\" || u.Hostname() == \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must be an absolute URL with a host\", provider)\n\t}\n\tif u.User != nil {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must not include userinfo\", provider)\n\t}\n\tif u.Fragment != \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must not include a fragment\", provider)\n\t}\n\tif err := validatePathComponents(u.Path, u.RawPath); err != nil {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL path rejected: %w\", provider, err)\n\t}\n\treturn u, nil\n}\n","sourceCodeStart":505,"sourceCodeEnd":536,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L505-L536","documentation":"The caveman proxy's OpenAI-compatible adapter validates the configured upstream base URL in parseBaseURL (openaicompat.go:522) before joining request paths onto it. This error fires when the URL either has no scheme (relative URL), or parses to an empty host (e.g. 'https:///v1' or 'http://'). The adapter cannot build a deterministic upstream endpoint without an absolute host, so it fails closed.","triggerScenarios":"Calling ResolveUpstreamURL or ValidateBaseURL when the provider's configured base URL is a relative path like '/v1', a scheme-only string like 'https://', or a URL where the host portion is empty (e.g. 'https:///api').","commonSituations":"A caveman.yaml compat-provider entry where the base_url was pasted without the 'https://' prefix, an env-var override (e.g. CAVE_..._BASE_URL) left partially filled, or a YAML value starting with '/' interpreted as a path. Also common when a trailing segment was accidentally kept and the host deleted during editing.","solutions":["Add the missing scheme and host so the value is a full absolute URL, e.g. change '/v1' to 'https://api.example.com/v1'.","Check the config file and any CAVE_*_BASE_URL environment variable overrides for a truncated or empty host segment and repair it.","Run the adapter's ValidateBaseURL (ValidateBaseURL(raw, \"compat\")) at startup or in a unit test to fail fast on the malformed value."],"exampleFix":"// before (caveman.yaml)\nbase_url: /v1\n# after\nbase_url: https://api.openai-compatible.example.com/v1","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(cfg.BaseURL))\nif err != nil || !u.IsAbs() || u.Host == \"\" || u.Hostname() == \"\" || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return fmt.Errorf(\"provider %q base URL must be an absolute http(s) URL with a host\", name)\n}","typeGuard":"func isValidBaseURL(raw string) bool {\n    u, err := url.Parse(strings.TrimSpace(raw))\n    return err == nil && u.IsAbs() && u.Host != \"\" && (u.Scheme == \"http\" || u.Scheme == \"https\")\n}","tryCatchPattern":"base, err := parseBaseURL(cfg.BaseURL, provider)\nif err != nil {\n    log.Fatalf(\"invalid base_url for %s: %v — expected https://host[:port]/path\", provider, err)\n}","preventionTips":["Always write base URLs with an explicit https:// scheme and host in config files.","Call ValidateBaseURL at startup so a bad base_url fails before the first request.","Never build a base URL by concatenating path fragments; keep scheme+host together.","Audit CAVE_*_BASE_URL environment overrides when config errors appear."],"tags":["go","url-validation","configuration","openaicompat"],"backgroundTag":"invalid-url-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}