JuliusBrussee/caveman · error
compat upstream base_url
Error message
compat upstream %q base_url: %w
What it means
NewNamedWithWireDialect validates the mount's base_url with ValidateBaseURL and wraps any failure as "compat upstream %q base_url: %w", naming the mount. A malformed base URL prevents constructing the adapter so routing to that upstream is refused at startup.
Solutions
- Set base_url to a full absolute URL including scheme, e.g. https://api.example.com.
- Trim stray whitespace/quotes from the config value or env interpolation.
- Check the message's mount name and fix that specific upstream's base_url entry.
- Validate the URL (net/url.Parse + scheme check) before writing it into config.
Example fix
# before base_url = "localhost:8080/v1" # after base_url = "https://localhost:8080/v1"
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(strings.TrimSpace(rawBaseURL))
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
return fmt.Errorf("base_url must be absolute http(s) URL, got %q", rawBaseURL)
} Try / catch
adapter, err := openaicompat.NewNamedWithWireDialect(name, baseURL, dialect)
if err != nil {
if strings.Contains(err.Error(), "base_url") {
return nil, fmt.Errorf("fix base_url for %s: %w", name, err)
}
return nil, err
} Prevention
- Always include http:// or https:// in base_url.
- Trim whitespace/quotes from env-interpolated values.
- Reject placeholder URLs (YOUR_HOST, example.com) in config linting.
When it happens
Trigger: baseURL after TrimSpace fails ValidateBaseURL: empty string, missing scheme (no http:// or https://), non-URL text, or a URL with disallowed components — passed via NewNamedWithWireDialect, NewNamed, or buildAdapters from config.
Common situations: Config with base_url = "localhost:8080" (no scheme); trailing whitespace or quotes left in the YAML/env value; placeholder URLs like "https://YOUR_API_HOST" not substituted; empty base_url for an optional mount.
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 prefix
- compat upstream name
- compat upstream name
- compat upstream forward_headers
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/88f8e3f3825319c0.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/providers/openaicompat/openaicompat.go:378
// the OpenAI-shape contradiction check reads a warm cache as malformed and
// drops every such row from token accounting (issue #1026).
// - COMPRESSION ZONES follow the Anthropic Messages grammar on Messages-path
// requests and keep the OpenAI grammar on every other path (see
// anthropicWireZones): the Anthropic extractor keys the live/frozen
// boundary on the request's own cache_control breakpoints.
//
// Routing, header mapping, telemetry provider, and pricing keep the mount's
// openai_compatible identity in every dialect.
func NewNamedWithWireDialect(name, baseURL, wireDialect string, forwardHeaders ...string) (providers.Adapter, error) {
if err := ValidateName(name); err != nil {
return nil, err
}
if err := ValidateWireDialect(wireDialect); err != nil {
return nil, fmt.Errorf("compat upstream %q: %w", name, err)
}
baseURL = strings.TrimSpace(baseURL)
if err := ValidateBaseURL(baseURL); err != nil {
return nil, fmt.Errorf("compat upstream %q base_url: %w", name, err)
}
if err := ValidateForwardHeaders(forwardHeaders); err != nil {
return nil, fmt.Errorf("compat upstream %q forward_headers: %w", name, err)
}
prefix := "/compat/" + name
return namedAdapter{
Base: providers.Base{
Provider: "openai_compatible",
BaseURL: baseURL,
Routes: []string{prefix + "/"},
UsageProvider: wireDialectUsageProvider[wireDialect],
},
prefix: prefix,
forwardHeaders: append([]string(nil), forwardHeaders...),
wireDialect: wireDialect,
}, nil
}
View on GitHub (pinned to 3ee70a1026)