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
- Use the request path that matches the adapter's configured prefix (a.prefix + "/")
- Check the CAVE_COMPAT_UPSTREAMS / compat: config and confirm the route name in the URL matches
- Update client base URLs to the current route prefix
- 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
- Keep client base URLs in sync with configured route names
- Validate route prefixes at startup with ValidateRequestPath
- Avoid renaming route prefixes without a migration window
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
- compat request URL is missing
- compat route does not match default /compat/ mount
- compat upstream base_url
- request URL is missing
- backslash is not allowed in path
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 nilView on GitHub (pinned to 3ee70a1026)