JuliusBrussee/caveman · error
upstream_proxy : unsupported scheme
Error message
upstream_proxy %q: unsupported scheme %q
What it means
The upstream_proxy config value was parsed as a URL but its scheme is not one of the four the proxy supports (http, https, socks5, socks5h). parseUpstreamProxy accepts "env", "off", or a full proxy URL, and rejects any other scheme because the HTTP transport layer cannot dial through it. This prevents silently configuring a proxy type the proxy will fail to use at request time.
Solutions
- Change the proxy URL scheme to one of http, https, socks5, or socks5h.
- If no scheme was intended, add the prefix explicitly, e.g. "http://proxy.corp:8080".
- If you wanted environment-based discovery instead, set upstream_proxy to "env".
- If no proxy is wanted, set upstream_proxy to "off".
Example fix
// before upstream_proxy = "socks4://127.0.0.1:9050" // after upstream_proxy = "socks5://127.0.0.1:9050"
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(raw)
if err != nil || u.Host == "" {
return fmt.Errorf("upstream_proxy %q must be \"env\", \"off\" or a proxy URL", raw)
}
switch u.Scheme {
case "http", "https", "socks5", "socks5h":
default:
return fmt.Errorf("unsupported upstream_proxy scheme %q", u.Scheme)
} Prevention
- Always include an explicit scheme prefix in proxy URLs (http://, socks5h://).
- Keep a config template with valid upstream_proxy examples.
- Validate proxy config in CI before deployment by calling Load.
When it happens
Trigger: Setting upstream_proxy to a URL whose scheme is not http/https/socks5/socks5h — e.g. "ftp://proxy:21", "quic://...", a URL with an empty scheme like "proxy.corp:8080" (parsed as opaque path, scheme = whole string), or typos like "https//proxy:8080" — then calling config.Load or UpstreamProxyFunc.
Common situations: Typing a host:port without a scheme prefix (url.Parse gives a bogus scheme); copying a PAC or browser proxy string; using a scheme variant like socks4 or http2 that is intentionally unsupported; YAML config edited by hand.
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 upstream name
- compat upstream name
- compat upstream base_url
- compat upstream forward_headers
- compat upstream
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/357e0370e1fe49eb.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/config/config.go:275
func parseUpstreamProxy(raw string) (func(*http.Request) (*url.URL, error), error) {
raw = strings.TrimSpace(raw)
switch strings.ToLower(raw) {
case "", "env":
// ProxyFromEnvironment snapshots the proxy variables once per process
// (sync.Once), so a test that t.Setenv's HTTPS_PROXY must build its own
// httpproxy.Config selector instead — see ssrf_test.go.
return http.ProxyFromEnvironment, nil
case "off":
return nil, nil
}
u, err := url.Parse(raw)
if err != nil || u.Host == "" {
return nil, fmt.Errorf("upstream_proxy %q must be \"env\", \"off\" or a proxy URL", raw)
}
switch u.Scheme {
case "http", "https", "socks5", "socks5h":
default:
return nil, fmt.Errorf("upstream_proxy %q: unsupported scheme %q", raw, u.Scheme)
}
// Same selector semantics as env mode: localhost/loopback destinations (an
// allowlisted Ollama) and NO_PROXY matches are dialed direct rather than
// handed to a corporate proxy that cannot reach them.
selector := (&httpproxy.Config{HTTPProxy: raw, HTTPSProxy: raw, NoProxy: env.String("NO_PROXY", env.String("no_proxy", ""))}).ProxyFunc()
return func(req *http.Request) (*url.URL, error) { return selector(req.URL) }, nil
}
// minAuthTokenBytes is the floor for the inbound shared secret. The token is the
// only gate in front of every configured provider credential once the proxy is
// reachable off-host, so a short one is not a weaker deployment, it is an open one.
const minAuthTokenBytes = 16
// validateAuthToken refuses a token that cannot survive one HTTP header value:
// control bytes terminate the field, and a space would split scheme from value in
// `Authorization: Bearer <token>`. The error never echoes the value — it is a
// secret and this message reaches the proxy log.
func validateAuthToken(token string) error {View on GitHub (pinned to 3ee70a1026)