{"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":251,"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":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/openaicompat/openaicompat.go#L233-L269","documentation":"Thrown by ValidateRequestPath when it is called with a nil *url.URL. This pre-adapter validation gate rejects ambiguous path encodings before adapter selection; a nil URL means the caller has no request path to validate, so it fails closed instead of skipping validation.","triggerScenarios":"ValidateRequestPath(nil) — a gateway or direct caller passing a nil URL because the request failed to parse upstream, or test code exercising the validation boundary without constructing a URL.","commonSituations":"Middleware that forwards r.URL after a parsing step that can return nil; tests calling the exported validator directly; refactoring that changed the call site from r.URL to a variable that can be nil.","solutions":["Pass the request's parsed URL: ValidateRequestPath(r.URL) on a non-nil *http.Request.","Add a nil check at the call site and reject the request earlier (400/502) if the URL could not be established.","In tests, build the URL with url.Parse or httptest.NewRequest rather than leaving it nil."],"exampleFix":"// before\nif err := ValidateRequestPath(maybeNilURL); err != nil { ... }\n\n// after\nif r == nil || r.URL == nil {\n    http.Error(w, \"bad request\", http.StatusBadRequest)\n    return\n}\nif err := ValidateRequestPath(r.URL); err != nil { ... }","handlingStrategy":"validation","validationCode":"if r == nil || r.URL == nil {\n    http.Error(w, \"request URL missing\", http.StatusBadRequest)\n    return\n}\nif err := openaicompat.ValidateRequestPath(r.URL); err != nil {\n    http.Error(w, err.Error(), http.StatusBadRequest)\n    return\n}","typeGuard":null,"tryCatchPattern":"Handle as a 400/500 boundary error and stop; no retry. Log where the nil URL came from if reachable.","preventionTips":["Never call ValidateRequestPath with a value that wasn't obtained from a parsed *http.Request.","Guard request middleware against nil r.URL before any handler runs.","Unit-test the exported validator with both nil and valid URLs to lock the contract."],"tags":["openai-compat","nil-guard","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}