{"record":{"id":"13e79d73b99b62b5","repo":"JuliusBrussee/caveman","slug":"provider-q-has-no-configured-upstream-url-13e79d","errorCode":null,"errorMessage":"provider %q has no configured upstream URL","messagePattern":"provider %q has no configured upstream URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":513,"sourceCode":"\t\t}\n\t\tif segment == \".\" || segment == \"..\" {\n\t\t\treturn fmt.Errorf(\"dot segments are not allowed in path\")\n\t\t}\n\t}\n\t// URL.Path is decoded by net/url while RawPath retains a valid escaped\n\t// spelling. Reject separators, backslashes, and dot bytes in either form so\n\t// a path cannot change route identity after another decoder or proxy hop.\n\tfor _, escape := range []string{\"%2f\", \"%5c\", \"%2e\"} {\n\t\tif strings.Contains(strings.ToLower(path), escape) || strings.Contains(strings.ToLower(rawPath), escape) {\n\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 {","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L495-L531","documentation":"parseBaseURL requires a non-empty BaseURL string for the named compat provider before it can compute the upstream target. An empty or whitespace-only value means the provider was registered without an upstream URL, so requests routed to it cannot be forwarded.","triggerScenarios":"A named compat upstream defined in compat: (standalone) or CAVE_COMPAT_UPSTREAMS (managed) with an empty BaseURL; env var entry present but the URL field blank; config parsed with missing keys defaulting to \"\"; calling ResolveUpstreamURL or ValidateBaseURL on a zero-value adapter.","commonSituations":"Typos in env var names so the URL field stays empty; partially written config committed before the URL was filled in; secrets managers returning empty strings for missing values.","solutions":["Set BaseURL for the provider in CAVE_COMPAT_UPSTREAMS / compat: config (e.g. https://api.example.com/v1)","Check the exact env var name and that it is exported in the environment","Restart the process after updating config so the new value is loaded","Remove the provider entry if it is not meant to be routed"],"exampleFix":"// before\nCAVE_COMPAT_UPSTREAMS=myprovider:  # no URL\n\n// after\nCAVE_COMPAT_UPSTREAMS=myprovider:https://api.myprovider.com/v1","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(cfg.BaseURL) == \"\" {\n    return fmt.Errorf(\"provider %q requires a non-empty upstream URL\", name)\n}","typeGuard":null,"tryCatchPattern":"if err := ValidateBaseURL(providerCfg.BaseURL); err != nil {\n    return fmt.Errorf(\"startup config invalid: %w\", err)\n}","preventionTips":["Validate all upstream configs at startup, not at request time","Fail fast with a clear message when env vars are empty","Use defaults or templated config so URL fields are never silently blank"],"tags":["go","config","proxy","missing-value"],"backgroundTag":"missing-required-config-field","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"}