JuliusBrussee/caveman · error
compat upstream forward_headers
Error message
compat upstream %q forward_headers: %w
What it means
NewNamedWithWireDialect validates the mount's forward_headers list with ValidateForwardHeaders and wraps failures as "compat upstream %q forward_headers: %w". Any header name that is not a valid HTTP header field name, starts with x-cave-/x-caveman-, or is in the denylist triggers this during mount construction.
Solutions
- Remove protected headers (auth, host, content-type, hop-by-hop, x-cave-*/x-caveman-*, x-forwarded-*) from forward_headers — authentication is handled by the credential mapper.
- Forward only safe custom headers your upstream actually needs (e.g. "x-request-id", "x-tenant").
- Use lowercase valid header names without spaces.
- Run ValidateForwardHeaders on the list before constructing the adapter to get a precise failing name.
Example fix
# before forward_headers = ["Authorization", "X-Request-Id"] # after forward_headers = ["X-Request-Id"]
Defensive patterns
Strategy: validation
Validate before calling
for _, h := range forwardHeaders {
if err := openaicompat.ValidateForwardHeaders([]string{h}); err != nil {
return fmt.Errorf("drop or rename %q: %w", h, err)
}
} Try / catch
if err := openaicompat.ValidateForwardHeaders(headers); err != nil {
return fmt.Errorf("filter forward_headers before mount creation: %w", err)
} Prevention
- Never list auth, content-type, user-agent, or hop-by-hop headers in forward_headers.
- Keep forward_headers to a minimal allowlist of custom x-* app headers.
- Lint config forward_headers lists in CI.
When it happens
Trigger: Passing forwardHeaders containing e.g. "Authorization", "X-Caveman-User", "Content-Type", "Host", or a syntactically invalid name (spaces, colon) to NewNamedWithWireDialect/NewNamed, or listing them in config forward_headers.
Common situations: Trying to forward credentials explicitly instead of using the credential mapper; copying curl-style header lists including Content-Type/User-Agent; hopping headers like Connection or Transfer-Encoding pasted from a debug session.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- header cannot be forwarded
- compat route path rejected
- compat upstream name
- compat upstream name
- compat upstream base_url
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/738ef0893425721a.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/providers/openaicompat/openaicompat.go:381
// 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
}
// ValidateForwardHeaders permits explicit provider-specific headers without
// letting a mount override routing, message framing, or Caveman credentials.
// Standard provider authentication is handled by the credential mapper.View on GitHub (pinned to 3ee70a1026)