{"record":{"id":"1badde3b52b03d68","repo":"JuliusBrussee/caveman","slug":"compat-request-url-is-missing","errorCode":null,"errorMessage":"compat request URL is missing","messagePattern":"compat request URL is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":226,"sourceCode":"\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\")\n\t}\n\tif err := validateCompatPath(req.URL.Path, req.URL.RawPath); err != nil {\n\t\treturn nil, err\n\t}\n\tbase, err := parseBaseURL(a.BaseURL, a.Provider)\n\tif err != nil {\n\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","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/openaicompat/openaicompat.go#L208-L244","documentation":"Thrown by namedAdapter.ResolveUpstreamURL when req is nil or req.URL is nil. Named compat adapters (independent static upstreams configured via compat: entries or CAVE_COMPAT_UPSTREAMS) need the request URL to validate the path and compute the upstream path, so a request with no URL fails closed instead of panicking.","triggerScenarios":"Invoking namedAdapter.ResolveUpstreamURL(nil, ...) or with a *http.Request whose URL field is nil — typically a hand-constructed request in tests or a middleware bug that drops the URL before the adapter runs.","commonSituations":"Unit tests constructing &http.Request{} without setting URL; a middleware chain that replaces the request with an incomplete clone; calling the adapter directly instead of through the gateway that always provides a parsed URL.","solutions":["Ensure the request passed to the adapter is a fully formed *http.Request with a non-nil URL (use http.NewRequest or httptest.NewRequest).","If writing direct-call code, guard with `if req == nil || req.URL == nil { ... }` before calling ResolveUpstreamURL.","Prefer calling the adapter through the gateway's normal request lifecycle, which always supplies a parsed URL."],"exampleFix":"// before\nreq := &http.Request{Header: hdr}\nu, err := adapter.ResolveUpstreamURL(ctx, req, route)\n\n// after\nreq := httptest.NewRequest(\"POST\", \"/compat/myupstream/v1/chat/completions\", body)\nu, err := adapter.ResolveUpstreamURL(ctx, req, route)","handlingStrategy":"validation","validationCode":"if req == nil || req.URL == nil {\n    return errors.New(\"cannot resolve upstream: request URL missing\")\n}\nu, err := adapter.ResolveUpstreamURL(ctx, req, route)","typeGuard":null,"tryCatchPattern":"Treat as an internal invariant violation: return 500 and log a stack trace; no retry. A nil URL here means a bug in the caller's middleware, not a transient fault.","preventionTips":["Always build requests with http.NewRequest / httptest.NewRequest so URL is never nil.","In middleware chains, propagate the original request rather than reconstructing partial clones.","Add a nil-request test case to any code that calls adapter ResolveUpstreamURL directly."],"tags":["openai-compat","nil-guard","api-misuse"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}