{"record":{"id":"487c21ca5b20b72f","repo":"JuliusBrussee/caveman","slug":"compat-upstream-name-q-is-reserved","errorCode":null,"errorMessage":"compat upstream name %q is reserved","messagePattern":"compat upstream name %q is reserved","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":426,"sourceCode":"\t\t// constructs. x-forwarded-*/forwarded/x-real-ip would let a caller\n\t\t// choose the client address an upstream rate-limits or allowlists on.\n\t\tcase \"host\", \"connection\", \"keep-alive\", \"proxy-connection\", \"proxy-authorization\", \"proxy-authenticate\", \"te\", \"trailer\", \"transfer-encoding\", \"upgrade\", \"content-length\", \"expect\",\n\t\t\t\"authorization\", \"x-api-key\", \"api-key\", \"x-goog-api-key\", \"x-goog-user-project\", \"cookie\", \"set-cookie\",\n\t\t\t\"forwarded\", \"x-forwarded-for\", \"x-forwarded-host\", \"x-forwarded-proto\", \"x-forwarded-port\", \"x-real-ip\",\n\t\t\t\"user-agent\", \"content-type\":\n\t\t\treturn fmt.Errorf(\"header %q cannot be forwarded\", name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ValidateName(name string) error {\n\tif !validName.MatchString(name) {\n\t\treturn fmt.Errorf(\"compat upstream name %q must match [a-z0-9][a-z0-9._-]{0,63}\", name)\n\t}\n\tswitch name {\n\tcase \"stub\", \"openai-compatible\":\n\t\treturn fmt.Errorf(\"compat upstream name %q is reserved\", name)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc ValidateBaseURL(raw string) error {\n\t_, err := parseBaseURL(raw, \"compat\")\n\treturn err\n}\n\n// ResolveUpstreamURL intentionally ignores RouteContext.BaseURL. Named mounts\n// are configured as independent static upstreams (`compat:` in standalone or\n// CAVE_COMPAT_UPSTREAMS in managed mode); applying the generic project\n// openai_compatible override here would collapse every named route onto one\n// target. The default /compat/ adapter is the route that honors BaseURL.\nfunc (a namedAdapter) ResolveUpstreamURL(_ context.Context, req *http.Request, _ providers.RouteContext) (*url.URL, error) {\n\tif req == nil || req.URL == nil {\n\t\treturn nil, fmt.Errorf(\"compat request URL is missing\")","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L408-L444","documentation":"After passing the name grammar, ValidateName rejects the reserved names \"stub\" and \"openai-compatible\", which are the built-in default compat mounts. Using them for a custom mount would collide with the built-in /compat/stub and /compat/openai-compatible routes.","triggerScenarios":"Configuring a named compat mount with name = \"stub\" or \"openai-compatible\" and loading it via validateCompat/NewNamedWithWireDialect.","commonSituations":"Copying the built-in default mount entry to customize it without renaming; config examples that use \"stub\" as a placeholder name left unchanged.","solutions":["Rename the mount to something unique, e.g. \"my-openai\" or \"acme-proxy\".","Keep using the built-in /compat/openai-compatible mount instead of defining a custom one with that name.","Use \"stub\" only for the built-in test stub, never in production config.","Run the startup config validation and rename any entry it flags as reserved."],"exampleFix":"# before\nname = \"openai-compatible\"\n# after\nname = \"my-openai-compatible\"","handlingStrategy":"validation","validationCode":"var reserved = map[string]bool{\"stub\": true, \"openai-compatible\": true}\nif reserved[mount.Name] {\n    return fmt.Errorf(\"mount name %q is reserved; pick a unique name\", mount.Name)\n}","typeGuard":null,"tryCatchPattern":"if err := openaicompat.ValidateName(name); err != nil {\n    return nil, fmt.Errorf(\"choose a non-reserved mount name: %w\", err)\n}","preventionTips":["Never name custom mounts \"stub\" or \"openai-compatible\".","Prefix custom mount names with your org, e.g. \"acme-openai\".","Keep built-in mounts and custom mounts in separate config sections."],"tags":["go","proxy","config","naming","reserved-names"],"backgroundTag":"invalid-identifier-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"}