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

  1. Add the scheme prefix to the proxy string, e.g. '127.0.0.1:8080' -> 'http://127.0.0.1:8080'.
  2. Ensure the host (and port) are present after the scheme, e.g. 'socks5://127.0.0.1:1080'.
  3. Pre-validate in your own code: parse the URL and check u.Scheme != "" && u.Host != "" before calling NewHTTPClient.
  4. 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

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)