{"record":{"id":"9a0f5abd60ad2431","repo":"JuliusBrussee/caveman","slug":"compat-upstream-q-w","errorCode":null,"errorMessage":"compat upstream %q: %w","messagePattern":"compat upstream %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/config/config.go","lineNumber":245,"sourceCode":"\t\t}\n\t}\n\treturn DefaultBedrockRegion\n}\n\n// BedrockBaseURL returns an explicit operator override or derives AWS's standard\n// 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\",","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/config/config.go#L227-L263","documentation":"Config.validateCompat wraps openaicompat.ValidateName for each entry in the compat map. A compat upstream name must match [a-z0-9][a-z0-9._-]{0,63} and must not be the reserved names 'stub' or 'openai-compatible', because the name becomes a route prefix mounted under /compat/. The wrapped error identifies which entry failed.","triggerScenarios":"compat: map key 'My Provider' (uppercase/space), 'grok/usa' (slash), '-leading' (starts with '-'), a name longer than 64 chars, or the reserved keys 'stub' or 'openai-compatible'.","commonSituations":"Pasting a vendor display name instead of a slug; using uppercase vendor names ('OpenRouter'); name collision with the built-in generic openai-compatible route.","solutions":["Rename the compat key to a lowercase slug: 'openrouter', 'together-ai', 'deepseek.v3'","Keep it under 64 characters, starting with a letter or digit, using only [a-z0-9._-]","Avoid the reserved names 'stub' and 'openai-compatible'"],"exampleFix":"# before\ncompat:\n  \"OpenRouter\":        # Error[1062]: invalid name\n    base_url: https://openrouter.ai/api/v1\n\n# after\ncompat:\n  openrouter:\n    base_url: https://openrouter.ai/api/v1","handlingStrategy":"validation","validationCode":"var compatNameRe = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,63}$`)\n\nfunc validCompatName(name string) bool {\n    if !compatNameRe.MatchString(name) {\n        return false\n    }\n    return name != \"stub\" && name != \"openai-compatible\"\n}\n\nfor name := range cfg.Compat {\n    if !validCompatName(name) { /* reject before Load() */ }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use lowercase slug names for compat upstreams","Reserve the built-in 'stub' and 'openai-compatible' identifiers","Validate generated config in CI by calling config.Load on the artifact"],"tags":["config","validation","compat","naming"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}