MHSanaei/3x-ui · error
parse proxy url: %w
Error message
parse proxy url: %w
What it means
NewHTTPClient in internal/util/netproxy/netproxy.go parses the admin-configured proxy URL with net/url.Parse and wraps any failure as 'parse proxy url: %w'. url.Parse only fails on genuinely malformed URLs (control characters, unmatched brackets, missing scheme with a colon misplaced), not on unknown schemes — those fail later at the scheme switch. Note this address is deliberately exempt from SSRF filtering because it is admin-configured.
Source
Thrown at internal/util/netproxy/netproxy.go:35
// NewHTTPClient returns an *http.Client whose transport honors proxyURL.
//
// An empty proxyURL yields a plain client (unchanged behavior). socks5/socks5h
// URLs are dialed through golang.org/x/net/proxy; http/https URLs use the
// standard library proxy support. Any other scheme returns an error so callers
// can log it and fall back to a direct connection.
//
// The proxy address is intentionally not subjected to SSRF filtering: it is
// admin-configured and is commonly a loopback/private address (for example a
// local Xray SOCKS inbound).
func NewHTTPClient(proxyURL string, timeout time.Duration) (*http.Client, error) {
if proxyURL == "" {
return &http.Client{Timeout: timeout}, nil
}
parsed, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("parse proxy url: %w", err)
}
transport := baseTransport()
switch strings.ToLower(parsed.Scheme) {
case "socks5", "socks5h":
var auth *proxy.Auth
if parsed.User != nil {
password, _ := parsed.User.Password()
auth = &proxy.Auth{User: parsed.User.Username(), Password: password}
}
dialer, err := proxy.SOCKS5("tcp", parsed.Host, auth, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("create socks5 dialer: %w", err)
}
if contextDialer, ok := dialer.(proxy.ContextDialer); ok {
transport.DialContext = contextDialer.DialContext
} else {View on GitHub (pinned to ad32144c42)
Solutions
- Re-enter the proxy URL exactly as scheme://[user:pass@]host:port, URL-encoding any special characters in user/password.
- Wrap IPv6 literals in brackets: socks5://[::1]:1080.
- Trim whitespace/newlines from the configured value before passing it in.
- Test the URL in isolation: url.Parse it in a scratch program to see the underlying error text carried by %w.
Example fix
// before proxyURL := "socks5://user:pa:ss@10.0.0.1:1080" // colon in password breaks parsing client, err := netproxy.NewHTTPClient(proxyURL, timeout) // after proxyURL := "socks5://user:pa%3Ass@10.0.0.1:1080" client, err := netproxy.NewHTTPClient(proxyURL, timeout)
Defensive patterns
Strategy: validation
Validate before calling
func validProxyURL(raw string) bool {
raw = strings.TrimSpace(raw)
if raw == "" { return true } // empty means direct
u, err := url.Parse(raw)
return err == nil && u.Scheme != "" && u.Host != ""
} Try / catch
client, err := netproxy.NewHTTPClient(proxyURL, timeout)
if err != nil {
return fmt.Errorf("bad proxy setting %q: %w", proxyURL, err)
} Prevention
- Validate the proxy URL at settings-save time, not at request time.
- URL-encode credentials containing ':' or '@'.
- Trim pasted values and reject embedded newlines on save.
When it happens
Trigger: Calling NewHTTPClient with a proxyURL containing control chars, spaces, 'socks5://user:pa:ss@1.2.3.4:1080' (colon inside password unescaped), 'http//[::1]:8080' (missing colon), or an empty scheme followed by junk. Set via whatever setting feeds this function (e.g. panel settings for outbound HTTP requests through a proxy).
Common situations: Typing a proxy URL in panel settings with an unencoded special character in the password; a trailing newline or space pasted from a terminal; IPv6 proxy address without brackets; env var with stray quotes.
Related errors
- create socks5 dialer: %w
- unsupported proxy scheme %q
- invalid host %q
- XUI_DB_TYPE=postgres but XUI_DB_DSN is empty
- destination DSN is required
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/5e63030767c4cfe6.
Report an issue: GitHub.