{"record":{"id":"88f8e3f3825319c0","repo":"JuliusBrussee/caveman","slug":"compat-upstream-q-base-url-w-88f8e3","errorCode":null,"errorMessage":"compat upstream %q base_url: %w","messagePattern":"compat upstream %q base_url: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":378,"sourceCode":"//     the OpenAI-shape contradiction check reads a warm cache as malformed and\n//     drops every such row from token accounting (issue #1026).\n//   - COMPRESSION ZONES follow the Anthropic Messages grammar on Messages-path\n//     requests and keep the OpenAI grammar on every other path (see\n//     anthropicWireZones): the Anthropic extractor keys the live/frozen\n//     boundary on the request's own cache_control breakpoints.\n//\n// Routing, header mapping, telemetry provider, and pricing keep the mount's\n// openai_compatible identity in every dialect.\nfunc NewNamedWithWireDialect(name, baseURL, wireDialect string, forwardHeaders ...string) (providers.Adapter, error) {\n\tif err := ValidateName(name); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := ValidateWireDialect(wireDialect); err != nil {\n\t\treturn nil, fmt.Errorf(\"compat upstream %q: %w\", name, err)\n\t}\n\tbaseURL = strings.TrimSpace(baseURL)\n\tif err := ValidateBaseURL(baseURL); err != nil {\n\t\treturn nil, fmt.Errorf(\"compat upstream %q base_url: %w\", name, err)\n\t}\n\tif err := ValidateForwardHeaders(forwardHeaders); err != nil {\n\t\treturn nil, fmt.Errorf(\"compat upstream %q forward_headers: %w\", name, err)\n\t}\n\tprefix := \"/compat/\" + name\n\treturn namedAdapter{\n\t\tBase: providers.Base{\n\t\t\tProvider:      \"openai_compatible\",\n\t\t\tBaseURL:       baseURL,\n\t\t\tRoutes:        []string{prefix + \"/\"},\n\t\t\tUsageProvider: wireDialectUsageProvider[wireDialect],\n\t\t},\n\t\tprefix:         prefix,\n\t\tforwardHeaders: append([]string(nil), forwardHeaders...),\n\t\twireDialect:    wireDialect,\n\t}, nil\n}\n","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L360-L396","documentation":"NewNamedWithWireDialect validates the mount's base_url with ValidateBaseURL and wraps any failure as \"compat upstream %q base_url: %w\", naming the mount. A malformed base URL prevents constructing the adapter so routing to that upstream is refused at startup.","triggerScenarios":"baseURL after TrimSpace fails ValidateBaseURL: empty string, missing scheme (no http:// or https://), non-URL text, or a URL with disallowed components — passed via NewNamedWithWireDialect, NewNamed, or buildAdapters from config.","commonSituations":"Config with base_url = \"localhost:8080\" (no scheme); trailing whitespace or quotes left in the YAML/env value; placeholder URLs like \"https://YOUR_API_HOST\" not substituted; empty base_url for an optional mount.","solutions":["Set base_url to a full absolute URL including scheme, e.g. https://api.example.com.","Trim stray whitespace/quotes from the config value or env interpolation.","Check the message's mount name and fix that specific upstream's base_url entry.","Validate the URL (net/url.Parse + scheme check) before writing it into config."],"exampleFix":"# before\nbase_url = \"localhost:8080/v1\"\n# after\nbase_url = \"https://localhost:8080/v1\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(rawBaseURL))\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") || u.Host == \"\" {\n    return fmt.Errorf(\"base_url must be absolute http(s) URL, got %q\", rawBaseURL)\n}","typeGuard":null,"tryCatchPattern":"adapter, err := openaicompat.NewNamedWithWireDialect(name, baseURL, dialect)\nif err != nil {\n    if strings.Contains(err.Error(), \"base_url\") {\n        return nil, fmt.Errorf(\"fix base_url for %s: %w\", name, err)\n    }\n    return nil, err\n}","preventionTips":["Always include http:// or https:// in base_url.","Trim whitespace/quotes from env-interpolated values.","Reject placeholder URLs (YOUR_HOST, example.com) in config linting."],"tags":["go","proxy","config","url"],"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"}