fish2018/pansou · error
代理地址必须包含协议和主机
Error message
代理地址必须包含协议和主机
What it means
After url.Parse succeeds, applyProxy requires the parsed proxy URL to have both a scheme and a host. This error is returned when the string parses but is missing one of them, e.g. '127.0.0.1:8080' (no scheme) or 'socks5://' (no host). Go's url.Parse is lenient, so this explicit check catches proxy strings that are not usable as proxy endpoints.
Solutions
- Add the scheme prefix to the proxy string, e.g. '127.0.0.1:8080' -> 'http://127.0.0.1:8080'.
- Ensure the host (and port) are present after the scheme, e.g. 'socks5://127.0.0.1:1080'.
- Pre-validate in your own code: parse the URL and check u.Scheme != "" && u.Host != "" before calling NewHTTPClient.
- If the value comes from config, document/enforce the full 'scheme://host:port' form.
Example fix
// before
client, err := NewHTTPClient(WithProxy("127.0.0.1:8080"))
// after
client, err := NewHTTPClient(WithProxy("http://127.0.0.1:8080")) Defensive patterns
Strategy: validation
Validate before calling
if u, err := url.Parse(cfg.Proxy); err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("proxy must be scheme://host:port, got %q", cfg.Proxy)
} Try / catch
if err := NewHTTPClient(WithProxy(cfg.Proxy)); err != nil {
if strings.Contains(err.Error(), "协议和主机") {
// fall back to direct connection or fix config
}
return err
} Prevention
- Never configure a proxy without an explicit scheme prefix.
- Remember url.Parse treats bare 'host:port' as scheme=host — always add 'http://'.
- Document the expected 'scheme://host:port' format wherever proxies are configured.
When it happens
Trigger: NewHTTPClient called with a proxy like '127.0.0.1:8080' (missing scheme), 'http://' (missing host), an empty-ish string with only a fragment, or 'localhost:8080' where 'localhost' is parsed as the scheme rather than the host.
Common situations: Users copying a browser PAC-style 'host:port' proxy setting into config; omitting 'http://' when configuring; env vars like HTTPS_PROXY set to a bare 'host:port' value; documentation examples that show proxies without a scheme.
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 fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/5a18a89393017bcd.
Report an issue: GitHub.
Appendix: source
Thrown at util/http_util.go:87
Transport: transport,
Timeout: time.Duration(60) * time.Second,
}
return client, nil
}
func applyProxy(transport *http.Transport, rawProxyURL string) error {
rawProxyURL = strings.TrimSpace(rawProxyURL)
if rawProxyURL == "" {
return nil
}
proxyURL, err := url.Parse(rawProxyURL)
if err != nil {
return fmt.Errorf("代理地址解析失败: %w", err)
}
if proxyURL.Scheme == "" || proxyURL.Host == "" {
return fmt.Errorf("代理地址必须包含协议和主机")
}
switch strings.ToLower(proxyURL.Scheme) {
case "socks5", "socks5h":
if proxyURL.Scheme == "socks5h" {
clone := *proxyURL
clone.Scheme = "socks5"
proxyURL = &clone
}
// 创建SOCKS5代理拨号器
dialer, err := proxy.FromURL(proxyURL, proxy.Direct)
if err != nil {
return fmt.Errorf("SOCKS5代理初始化失败: %w", err)
}
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)View on GitHub (pinned to beaa561337)