JuliusBrussee/caveman · error

compat route does not match prefix

Error message

compat route %q does not match prefix %q

What it means

Named compat routes (CAVE_COMPAT_UPSTREAMS / compat: standalone entries) each own a path prefix such as /compat/<name>/. ResolveUpstreamURL refuses to rewrite a request whose path does not start with that adapter's prefix, because the request was routed to the wrong adapter and its path cannot be safely translated to the upstream base URL.

Solutions

  1. Use the request path that matches the adapter's configured prefix (a.prefix + "/")
  2. Check the CAVE_COMPAT_UPSTREAMS / compat: config and confirm the route name in the URL matches
  3. Update client base URLs to the current route prefix
  4. Route the request to the correct adapter for its prefix

Example fix

// before
POST /compat/anthropic/v1/chat/completions  -> routed to adapter with prefix "/compat/openai"

// after
POST /compat/openai/v1/chat/completions  -> routed to matching adapter
Defensive patterns

Strategy: validation

Validate before calling

if !strings.HasPrefix(req.URL.Path, prefix+"/") {
    return fmt.Errorf("path %q is not on route %q", req.URL.Path, prefix)
}

Try / catch

u, err := adapter.ResolveUpstreamURL(ctx, req, routeCtx)
if err != nil {
    if strings.Contains(err.Error(), "does not match prefix") {
        // select the adapter whose prefix matches req.URL.Path instead
    }
    return err
}

Prevention

When it happens

Trigger: Sending a request whose path is not <prefix>/... to a named compat adapter (e.g. /compat/other-provider/chat/completions resolved against adapter 'a'); manually calling ResolveUpstreamURL with a foreign path; a routing change renaming a prefix while clients still use the old one.

Common situations: Renaming or removing a named upstream in config while cached clients still target the old prefix; misconfigured client base URL pointing at the wrong route name; custom middleware rewriting paths before adapter resolution.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/d4eb230f89d49aea. Report an issue: GitHub.

Appendix: source

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

// 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
}

// ValidateRequestPath rejects ambiguous path encodings before adapter selection.
// Gateways call this with the original URL (including RawPath), while the
// adapter MatchRoute/ResolveUpstreamURL checks provide a second fail-closed
// boundary for direct callers.
func ValidateRequestPath(u *url.URL) error {
	if u == nil {
		return fmt.Errorf("request URL is missing")
	}
	if !strings.HasPrefix(u.Path, "/compat/") {
		return nil

View on GitHub (pinned to 3ee70a1026)