JuliusBrussee/caveman · error

provider upstream URL scheme is not allowed

Error message

provider %q upstream URL scheme %q is not allowed

What it means

parseBaseURL enforces that each upstream BaseURL is an absolute http(s) URL with a host, and rejects any other scheme (file:, unix:, ftp:, ws:, etc.). Only http and https are safe to hand to the proxy's HTTP client, so anything else fails validation at config time or request time.

Solutions

  1. Prefix the BaseURL with https:// (or http:// for plain-HTTP local upstreams)
  2. Use the form http://127.0.0.1:8080 for local services instead of a bare host:port or unix: scheme
  3. Run the config through ValidateBaseURL at startup to catch bad values before traffic
  4. If you need a unix socket, use a local HTTP proxy that bridges to it rather than a non-http scheme

Example fix

// before
BaseURL: "localhost:8080"

// after
BaseURL: "http://localhost:8080"
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(strings.TrimSpace(raw))
if err != nil {
    return err
}
if (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
    return fmt.Errorf("upstream must be an absolute http(s) URL")
}

Try / catch

if err := ValidateBaseURL(cfg.BaseURL); err != nil {
    return fmt.Errorf("invalid upstream URL for %s: %w", cfg.Provider, err)
}

Prevention

When it happens

Trigger: BaseURL like file:///var/run/sock, localhost:8080 (scheme parsed as 'localhost'), ftp://..., or a URL with an unexpected scheme in CAVE_COMPAT_UPSTREAMS / compat: config; values missing the '://' separator entirely.

Common situations: Omitting https:// when writing the config (host-only strings); trying to point the upstream at a unix socket via a non-http scheme; copying internal URLs with custom schemes.

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/5abcb9739c468e7f. Report an issue: GitHub.

Appendix: source

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

	// a path cannot change route identity after another decoder or proxy hop.
	for _, escape := range []string{"%2f", "%5c", "%2e"} {
		if strings.Contains(strings.ToLower(path), escape) || strings.Contains(strings.ToLower(rawPath), escape) {
			return fmt.Errorf("ambiguous escaped path sequence %s", escape)
		}
	}
	return nil
}

func parseBaseURL(raw, provider string) (*url.URL, error) {
	if strings.TrimSpace(raw) == "" {
		return nil, fmt.Errorf("provider %q has no configured upstream URL", provider)
	}
	u, err := url.Parse(strings.TrimSpace(raw))
	if err != nil {
		return nil, err
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return nil, fmt.Errorf("provider %q upstream URL scheme %q is not allowed", provider, u.Scheme)
	}
	if !u.IsAbs() || u.Host == "" || u.Hostname() == "" {
		return nil, fmt.Errorf("provider %q upstream URL must be an absolute URL with a host", provider)
	}
	if u.User != nil {
		return nil, fmt.Errorf("provider %q upstream URL must not include userinfo", provider)
	}
	if u.Fragment != "" {
		return nil, fmt.Errorf("provider %q upstream URL must not include a fragment", provider)
	}
	if err := validatePathComponents(u.Path, u.RawPath); err != nil {
		return nil, fmt.Errorf("provider %q upstream URL path rejected: %w", provider, err)
	}
	return u, nil
}

View on GitHub (pinned to 3ee70a1026)