{"record":{"id":"fb3c13ffda048dd2","repo":"JuliusBrussee/caveman","slug":"request-url-is-missing","errorCode":null,"errorMessage":"request URL is missing","messagePattern":"request URL is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":469,"sourceCode":"\t\treturn nil, err\n\t}\n\tif !strings.HasPrefix(req.URL.Path, a.prefix+\"/\") {\n\t\treturn nil, fmt.Errorf(\"compat route %q does not match prefix %q\", req.URL.Path, a.prefix+\"/\")\n\t}\n\tpath := strings.TrimPrefix(req.URL.Path, a.prefix)\n\tbase.Path = joinCompatPath(base.Path, path)\n\tbase.RawPath = \"\"\n\tbase.RawQuery = joinCompatQuery(base.RawQuery, req.URL.RawQuery)\n\treturn base, nil\n}\n\n// ValidateRequestPath rejects ambiguous path encodings before adapter selection.\n// Gateways call this with the original URL (including RawPath), while the\n// adapter MatchRoute/ResolveUpstreamURL checks provide a second fail-closed\n// boundary for direct callers.\nfunc ValidateRequestPath(u *url.URL) error {\n\tif u == nil {\n\t\treturn fmt.Errorf(\"request URL is missing\")\n\t}\n\tif !strings.HasPrefix(u.Path, \"/compat/\") {\n\t\treturn nil\n\t}\n\treturn validateCompatPath(u.Path, u.RawPath)\n}\n\nfunc validateCompatPath(path, rawPath string) error {\n\tif !strings.HasPrefix(path, \"/compat/\") {\n\t\treturn nil\n\t}\n\tif err := validatePathComponents(path, rawPath); err != nil {\n\t\treturn fmt.Errorf(\"compat route path rejected: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc validatePathComponents(path, rawPath string) error {","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L451-L487","documentation":"ValidateRequestPath checks the original request URL (including RawPath) for ambiguous encodings before adapter selection. It returns this error when called with a nil *url.URL, meaning the caller has no URL to validate. Gateways call it up front; the adapter checks are a second fail-closed boundary for direct callers.","triggerScenarios":"Calling ValidateRequestPath(nil); passing a URL obtained from a request whose URL was never populated; calling before parsing the raw request URI.","commonSituations":"Gateway middleware invoked on malformed requests where url.Parse failed earlier; tests passing nil directly; proxy hops dropping the URL field.","solutions":["Parse the incoming request URI with url.ParseRequestURI and check its error before validating","Ensure req.URL is populated before gateway validation","Skip/short-circuit when there is genuinely no URL, but reject the HTTP request with 400","In tests, always construct a URL via url.Parse"],"exampleFix":"// before\nerr := ValidateRequestPath(req.URL)\n\n// after\nif req == nil || req.URL == nil {\n    return fmt.Errorf(\"bad request: missing URL\")\n}\nerr := ValidateRequestPath(req.URL)","handlingStrategy":"validation","validationCode":"if req == nil || req.URL == nil {\n    return errors.New(\"missing request URL\")\n}\nif err := ValidateRequestPath(req.URL); err != nil {\n    return err\n}","typeGuard":"func hasRequestURL(req *http.Request) bool { return req != nil && req.URL != nil }","tryCatchPattern":"if err := ValidateRequestPath(req.URL); err != nil {\n    http.Error(w, \"invalid request path\", http.StatusBadRequest)\n    return\n}","preventionTips":["Run ValidateRequestPath in gateway middleware before adapter selection","Reject requests whose URL failed to parse early with a 400"],"tags":["go","proxy","url","validation"],"backgroundTag":"null-argument","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"}