JuliusBrussee/caveman · error

compat request URL is missing

Error message

compat request URL is missing

What it means

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.

Source

Thrown at proxy/providers/openaicompat/openaicompat.go:226

		return fmt.Errorf("compat upstream name %q is reserved", name)
	default:
		return nil
	}
}

func ValidateBaseURL(raw string) error {
	_, err := parseBaseURL(raw, "compat")
	return err
}

// ResolveUpstreamURL intentionally ignores RouteContext.BaseURL. Named mounts
// are configured as independent static upstreams (`compat:` in standalone or
// CAVE_COMPAT_UPSTREAMS in managed mode); applying the generic project
// openai_compatible override here would collapse every named route onto one
// target. The default /compat/ adapter is the route that honors BaseURL.
func (a namedAdapter) ResolveUpstreamURL(_ context.Context, req *http.Request, _ providers.RouteContext) (*url.URL, error) {
	if req == nil || req.URL == nil {
		return nil, fmt.Errorf("compat request URL is missing")
	}
	if err := validateCompatPath(req.URL.Path, req.URL.RawPath); err != nil {
		return nil, err
	}
	base, err := parseBaseURL(a.BaseURL, a.Provider)
	if err != nil {
		return nil, err
	}
	if !strings.HasPrefix(req.URL.Path, a.prefix+"/") {
		return nil, fmt.Errorf("compat route %q does not match prefix %q", req.URL.Path, a.prefix+"/")
	}
	path := strings.TrimPrefix(req.URL.Path, a.prefix)
	base.Path = joinCompatPath(base.Path, path)
	base.RawPath = ""
	base.RawQuery = joinCompatQuery(base.RawQuery, req.URL.RawQuery)
	return base, nil
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Ensure the request passed to the adapter is a fully formed *http.Request with a non-nil URL (use http.NewRequest or httptest.NewRequest).
  2. If writing direct-call code, guard with `if req == nil || req.URL == nil { ... }` before calling ResolveUpstreamURL.
  3. Prefer calling the adapter through the gateway's normal request lifecycle, which always supplies a parsed URL.

Example fix

// before
req := &http.Request{Header: hdr}
u, err := adapter.ResolveUpstreamURL(ctx, req, route)

// after
req := httptest.NewRequest("POST", "/compat/myupstream/v1/chat/completions", body)
u, err := adapter.ResolveUpstreamURL(ctx, req, route)
Defensive patterns

Strategy: validation

Validate before calling

if req == nil || req.URL == nil {
    return errors.New("cannot resolve upstream: request URL missing")
}
u, err := adapter.ResolveUpstreamURL(ctx, req, route)

Try / catch

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.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/1badde3b52b03d68. Report an issue: GitHub.