JuliusBrussee/caveman · error

compat route %q does not match default /compat/ mount

Error message

compat route %q does not match default /compat/ mount

What it means

Thrown by the default OpenAI-compatible adapter's ResolveUpstreamURL when the incoming request path does not start with /compat/. This adapter exists only for the bare public /compat/ mount; requests arriving on any other path indicate a routing/mount misconfiguration and are rejected rather than proxied to a guessed upstream.

Source

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

// keeps direct adapter users fail-closed as well.
func (a Adapter) MatchRoute(method, path string) bool {
	if err := validateCompatPath(path, ""); err != nil {
		return false
	}
	return a.Base.MatchRoute(method, path)
}

// ResolveUpstreamURL removes only the default public /compat mount before
// resolving the provider path. Named adapters and the reserved legacy routes
// keep their dedicated resolver/compatibility behavior; this method exists for
// the bare providers.openai_compatible route only.
func (a Adapter) ResolveUpstreamURL(_ context.Context, req *http.Request, route providers.RouteContext) (*url.URL, error) {
	if req == nil || req.URL == nil || !strings.HasPrefix(req.URL.Path, "/compat/") {
		path := ""
		if req != nil && req.URL != nil {
			path = req.URL.Path
		}
		return nil, fmt.Errorf("compat route %q does not match default /compat/ mount", path)
	}
	if err := validateCompatPath(req.URL.Path, req.URL.RawPath); err != nil {
		return nil, err
	}

	// /compat/stub and /compat/openai-compatible were the original default
	// routes. They share the same strict path and query handling as the bare
	// mount, but retain their historical prefix stripping.
	requestPath := strings.TrimPrefix(req.URL.Path, "/compat")
	for _, legacy := range []string{"/stub", "/openai-compatible"} {
		if hasCompatPrefix(req.URL.Path, "/compat"+legacy) {
			requestPath = strings.TrimPrefix(req.URL.Path, "/compat"+legacy)
			break
		}
	}

	baseURL := a.Base.BaseURL
	if route.BaseURL != "" {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Route the request through the /compat/ mount so the path retains the /compat/ prefix when ResolveUpstreamURL runs.
  2. For named mounts (e.g. /compat/<name>), use the namedAdapter registered for that mount — it resolves via its own configured BaseURL, not this default adapter.
  3. Check gateway routing config: the bare providers.openai_compatible route must map to the default adapter, custom routes to named adapters.

Example fix

// before: default adapter given a bare route
url, err := defaultAdapter.ResolveUpstreamURL(ctx, req, route) // req.URL.Path = "/v1/chat/completions"

// after: request reaches the adapter on the public mount
// req.URL.Path = "/compat/v1/chat/completions"
url, err := defaultAdapter.ResolveUpstreamURL(ctx, req, route)
Defensive patterns

Strategy: validation

Validate before calling

func isDefaultCompatMount(path string) bool {
    return strings.HasPrefix(path, "/compat/")
}

// before dispatching to the default adapter:
if !isDefaultCompatMount(req.URL.Path) {
    return fmt.Errorf("route %q must go to a named adapter, not the default /compat/ adapter", req.URL.Path)
}

Try / catch

Map to an HTTP routing error (404/421) at the gateway layer; log the offending path. Deterministic misroute — retrying cannot help.

Prevention

When it happens

Trigger: Calling Adapter.ResolveUpstreamURL with req.URL.Path like "/v1/chat/completions", "/", "", or a named-mount path like "/compat-foo/..." — anything without the "/compat/" prefix. Also fires when req or req.URL is nil (the guard reports the empty/blank path).

Common situations: A gateway route table sending openai_compatible traffic to the default adapter instead of a named adapter; direct callers reusing the default adapter for a custom mount path; a rewrite rule stripping /compat/ before the adapter runs.

Related errors


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