{"record":{"id":"e8416d745dc89c43","repo":"JuliusBrussee/caveman","slug":"compat-upstream-q-base-url-w","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/internal/config/config.go","lineNumber":251,"sourceCode":"// Runtime endpoint from BedrockRegion. Callers never need to paste a raw URL for\n// the normal first-party path.\nfunc (c Config) BedrockBaseURL() string {\n\tif configured := c.BaseURL(\"bedrock\", \"\"); configured != \"\" {\n\t\treturn configured\n\t}\n\treturn fmt.Sprintf(\"https://bedrock-runtime.%s.amazonaws.com\", c.BedrockRegion())\n}\n\nfunc (c Config) validateCompat() error {\n\tfor name, upstream := range c.Compat {\n\t\tif err := openaicompat.ValidateName(name); err != nil {\n\t\t\treturn fmt.Errorf(\"compat upstream %q: %w\", name, err)\n\t\t}\n\t\tif strings.TrimSpace(upstream.BaseURL) == \"\" {\n\t\t\treturn fmt.Errorf(\"compat upstream %q: base_url is required\", name)\n\t\t}\n\t\tif err := openaicompat.ValidateBaseURL(upstream.BaseURL); err != nil {\n\t\t\treturn fmt.Errorf(\"compat upstream %q: base_url: %w\", name, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// providerEnvKey maps a provider name to the BYOK environment variable that\n// holds its API key.\nvar providerEnvKey = map[string]string{\n\t\"anthropic\":         \"ANTHROPIC_API_KEY\",\n\t\"openai\":            \"OPENAI_API_KEY\",\n\t\"gemini\":            \"GEMINI_API_KEY\",\n\t\"azure_openai\":      \"AZURE_OPENAI_API_KEY\",\n\t\"openai_compatible\": \"OPENAI_COMPAT_API_KEY\",\n}\n\n// Credential returns the provider credential resolved from the environment.\n// Empty means the caller must fail closed or use an explicit inbound credential.\n// Bedrock bearer keys are PAYG credentials even though their wire scheme is","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/config/config.go#L233-L269","documentation":"validateCompat passes base_url through openaicompat.ValidateBaseURL, which parses it as an absolute URL for a compat upstream. The %w chain preserves the underlying parse error, so the message tells you exactly why the URL is unusable (missing scheme, unparseable, wrong scheme).","triggerScenarios":"base_url like 'openrouter.ai/api/v1' (no scheme), 'ftp://...' (wrong scheme), a URL with spaces or control characters, or one that net/url.Parse rejects (e.g. embedded credentials with invalid escapes).","commonSituations":"Omitting https:// when pasting from a browser bar; trailing '/v1' path confusion is fine but scheme omission is not; env var with stray quotes or newline characters.","solutions":["Prefix the scheme: base_url: https://openrouter.ai/api/v1","Use http:// only for deliberate local test doubles; https:// for real upstreams","Trim stray quotes, spaces, or newlines from env-provided URLs","Read the wrapped (%w) parse error text — it names the exact URL defect"],"exampleFix":"# before\ncompat:\n  openrouter:\n    base_url: openrouter.ai/api/v1   # Error[1064]: no scheme\n\n# after\ncompat:\n  openrouter:\n    base_url: https://openrouter.ai/api/v1","handlingStrategy":"validation","validationCode":"func usableBaseURL(raw string) bool {\n    u, err := url.Parse(strings.TrimSpace(raw))\n    return err == nil && u.IsAbs() && (u.Scheme == \"http\" || u.Scheme == \"https\") && u.Host != \"\"\n}\n\nfor name, up := range cfg.Compat {\n    if !usableBaseURL(up.BaseURL) { /* reject config */ }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the https:// scheme when pasting provider URLs","Trim and sanity-check env-provided URLs before writing config","Run ValidateBaseURL (openaicompat package) on generated configs"],"tags":["config","compat","url","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}