{"record":{"id":"e4c2384cf22e77ef","repo":"JuliusBrussee/caveman","slug":"compat-upstream-name-q-must-match-a-z0-9-a-z0-9","errorCode":null,"errorMessage":"compat upstream name %q must match [a-z0-9][a-z0-9._-]{0,63}","messagePattern":"compat upstream name %q must match \\[a-z0-9\\]\\[a-z0-9\\._-\\](.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":422,"sourceCode":"\t\t\treturn fmt.Errorf(\"header %q cannot be forwarded\", name)\n\t\t}\n\t\tswitch lower {\n\t\t// Routing/framing, credentials, and fields whose value this proxy\n\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","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L404-L440","documentation":"ValidateName enforces the mount-name grammar [a-z0-9][a-z0-9._-]{0,63}: lowercase alphanumeric first character, then lowercase alphanumerics, dots, underscores, or hyphens, max 64 chars. Names outside this grammar fail with this message because the name becomes a URL path segment (/compat/<name>/) and must be safe and unambiguous.","triggerScenarios":"A mount name with uppercase letters, spaces, a leading dot/hyphen/underscore, non-ASCII characters, or length > 64 is passed to NewNamedWithWireDialect or listed in config and checked by validateCompat.","commonSituations":"Human-readable names like \"My Upstream\" or \"Claude_Prod\"; names auto-generated with a UUID prefix; env-interpolated names carrying whitespace.","solutions":["Rename the mount to lowercase alphanumerics with . _ - separators, e.g. \"claude-prod\".","Ensure the first character is a lowercase letter or digit.","Shorten names to 64 characters or fewer.","Slugify the name (strings.ToLower + replace invalid chars) in config generation tooling."],"exampleFix":"# before\nname = \"Claude_Prod EU\"\n# after\nname = \"claude-prod-eu\"","handlingStrategy":"validation","validationCode":"var validName = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,63}$`)\nif !validName.MatchString(mount.Name) {\n    return fmt.Errorf(\"mount name %q must match [a-z0-9][a-z0-9._-]{0,63}\", mount.Name)\n}","typeGuard":null,"tryCatchPattern":"if err := openaicompat.ValidateName(name); err != nil {\n    return nil, fmt.Errorf(\"rename mount: %w\", err)\n}","preventionTips":["Slugify mount names: lowercase, no spaces, first char alphanumeric.","Cap names at 64 characters.","Validate names in config CI before deploy."],"tags":["go","proxy","config","naming","validation"],"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"}