JuliusBrussee/caveman · error

compat route does not match default /compat/ mount

Error message

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

What it means

The openaicompat adapter only serves requests mounted under the /compat/ path prefix; ResolveUpstreamURL rejects any request whose URL path does not start with /compat/. The error reports the offending path so you can see which route was misrouted to the compat provider.

Solutions

  1. Route the request through a path beginning with /compat/ (e.g. /compat/<mount-name>/v1/...).
  2. If you need a custom prefix, use the named-mount mechanism (NewNamedWithWireDialect / config named mounts) instead of relying on the default mount.
  3. Check any rewriting middleware (ingress, strip-prefix) is not removing the /compat/ segment.
  4. Verify the route is being dispatched to the correct adapter, not the compat one, if the path is intentionally non-compat.

Example fix

// before
http.Post("http://proxy/v1/chat/completions", ...)
// after
http.Post("http://proxy/compat/openai-compatible/v1/chat/completions", ...)
Defensive patterns

Strategy: validation

Validate before calling

func isCompatRoute(u *url.URL) bool {
    return u != nil && strings.HasPrefix(u.Path, "/compat/")
}

Try / catch

upstream, err := adapter.ResolveUpstreamURL(ctx, req, routeCtx)
if err != nil && strings.Contains(err.Error(), "does not match default /compat/ mount") {
    return fmt.Errorf("rewrite request to /compat/<mount>/... before dispatch: %w", err)
}

Prevention

When it happens

Trigger: An http.Request whose URL.Path lacks the /compat/ prefix (or a nil request/URL) reaches the compat adapter's ResolveUpstreamURL during route resolution.

Common situations: Mounting the openai_compatible adapter on a custom route prefix other than /compat/; a reverse-proxy rewrite strips /compat/ before the request hits the provider; programmatic adapter use invoking ResolveUpstreamURL directly with a raw path.

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/18572442082d13e9. Report an issue: GitHub.

Appendix: source

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

// 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 3ee70a1026)