gorilla/websocket · error
f[1] (proxy response status text)
Error message
f[1] (proxy response status text)
What it means
This error is returned by DialContext when an HTTP proxy responds to the CONNECT request with a status code other than 200. The library closes the connection and surfaces the proxy's own status message (e.g. '407 Proxy Authentication Required' → 'Proxy Authentication Required') as the error text. It means the proxy refused to open the tunnel to the target host, not that the WebSocket handshake itself failed.
Solutions
- Check the status message in the error (it is the proxy's status text) to identify why the tunnel was refused
- If it is a 407, embed credentials in the proxy URL, e.g. http://user:pass@proxy:8080, or configure them via HTTPS_PROXY, since gorilla only supports Basic proxy auth
- Verify with curl -x $HTTPS_PROXY https://target-host that the proxy can reach the target; fix proxy ACLs/whitelisting if blocked
- Confirm env vars (HTTPS_PROXY/HTTP_PROXY/NO_PROXY) point at the correct reachable proxy, or unset Proxy in the Dialer if no proxy is needed
- Retry later or use a different proxy if the status is 502/503 (upstream outage)
Example fix
// before (407: proxy needs auth)
u := websocket.Upgrader{}
_, _, err := websocket.DefaultDialer.DialContext(ctx, "wss://example.com/ws", nil)
// err: "Proxy Authentication Required"
// after: supply Basic proxy credentials in the proxy URL
proxyURL, _ := url.Parse("http://user:pass@proxy.corp:8080")
dialer := &websocket.Dialer{
Proxy: http.ProxyURL(proxyURL),
HandshakeTimeout: 10 * time.Second,
}
conn, _, err := dialer.DialContext(ctx, "wss://example.com/ws", nil) Defensive patterns
Strategy: retry
Validate before calling
proxyURL, err := http.ProxyFromEnvironment(&http.Request{URL: targetURL})
if err != nil {
return nil, fmt.Errorf("bad proxy config: %w", err)
}
if proxyURL != nil && proxyURL.User == nil && os.Getenv("HTTPS_PROXY") != "" {
log.Printf("warning: proxy %s has no credentials; CONNECT may return 407", proxyURL.Host)
} Type guard
func isProxyStatusError(err error) bool {
if err == nil {
return false
}
for _, code := range []int{403, 407, 502, 503} {
if strings.Contains(err.Error(), http.StatusText(code)) {
return true
}
}
return false
} Try / catch
conn, _, err := dialer.DialContext(ctx, wsURL, nil)
if err != nil {
if isProxyStatusError(err) {
if strings.Contains(err.Error(), http.StatusText(407)) {
// fix proxy credentials, then retry once
dialer.Proxy = http.ProxyURL(proxyWithAuth)
conn, _, err = dialer.DialContext(ctx, wsURL, nil)
}
// 502/503: retry with backoff
if backoff := retryWithBackoff(ctx); backoff != nil {
conn, _, err = dialer.DialContext(ctx, wsURL, nil)
}
}
if err != nil {
return fmt.Errorf("ws dial failed: %w", err)
}
} Prevention
- Put credentials in the proxy URL (user:pass@host) before deploying behind a corporate proxy
- Prefer http.ProxyFromEnvironment and keep HTTPS_PROXY/NO_PROXY correct per environment
- Smoke-test the CONNECT path with curl -x $HTTPS_PROXY https://target in CI
- Support only proxies that allow Basic auth, since gorilla has no NTLM/Kerberos support
- Add a short HandshakeTimeout so proxy failures fail fast instead of hanging
When it happens
Trigger: dialer.Dial/DialContext called with Proxy set (http.ProxyFromEnvironment or custom) where the proxy answers the CONNECT with e.g. 407 (missing proxy credentials), 403 (target blocked by policy), 502/503 (proxy cannot reach target or is overloaded).
Common situations: Corporate environments where HTTPS_PROXY is set but credentials are missing from the proxy URL (user:pass@host) or the proxy requires NTLM/Kerberos auth gorilla does not support; VPN/network changes making the proxy unreachable to the target; proxy ACLs blocking the destination port 443/80.
Related errors
- websocket: bad handshake
- concurrent write to websocket connection
- http.StatusText(status)
- malformed ws or wss URL
- repeated read on failed websocket connection
AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31).
Data as JSON: /api/errors/a6a8c0211169ee66.
Report an issue: GitHub.
Appendix: source
Thrown at proxy.go:101
if err != nil {
conn.Close()
return nil, err
}
// Close the response body to silence false positives from linters. Reset
// the buffered reader first to ensure that Close() does not read from
// conn.
// Note: Applications must call resp.Body.Close() on a response returned
// http.ReadResponse to inspect trailers or read another response from the
// buffered reader. The call to resp.Body.Close() does not release
// resources.
br.Reset(bytes.NewReader(nil))
_ = resp.Body.Close()
if resp.StatusCode != http.StatusOK {
_ = conn.Close()
f := strings.SplitN(resp.Status, " ", 2)
return nil, errors.New(f[1])
}
return conn, nil
}
View on GitHub (pinned to e064f32e36)