grpc/grpc-go · error
failed to do connect handshake, status code: %s
Error message
failed to do connect handshake, status code: %s
What it means
This error occurs in doHTTPConnectHandshake when the proxy returns a non-200 status to the CONNECT request AND httputil.DumpResponse also fails (so only the raw status string is included). This means the proxy explicitly refused the tunnel and the response body could not be dumped for diagnostics.
Source
Thrown at internal/transport/proxy.go:87
if user := opts.User; user != nil {
u := user.Username()
p, _ := user.Password()
req.Header.Add(proxyAuthHeaderKey, "Basic "+basicAuth(u, p))
}
if err := sendHTTPRequest(ctx, req, conn); err != nil {
return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
}
r := bufio.NewReader(conn)
resp, err := http.ReadResponse(r, req)
if err != nil {
return nil, fmt.Errorf("reading server HTTP response: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
dump, err := httputil.DumpResponse(resp, true)
if err != nil {
return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status)
}
return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump)
}
// The buffer could contain extra bytes from the target server, so we can't
// discard it. However, in many cases where the server waits for the client
// to send the first message (e.g. when TLS is being used), the buffer will
// be empty, so we can avoid the overhead of reading through this buffer.
if r.Buffered() != 0 {
return &bufConn{Conn: conn, r: r}, nil
}
return conn, nil
}
// proxyDial establishes a TCP connection to the specified address and performs an HTTP CONNECT handshake.
func proxyDial(ctx context.Context, addr resolver.Address, grpcUA string, opts proxyattributes.Options) (net.Conn, error) {
conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", addr.Addr)
if err != nil {
return nil, errView on GitHub (pinned to 03255a9237)
Solutions
- Check the HTTP status code in the error to determine the proxy's refusal reason (407 = auth, 502 = upstream failure, 403 = policy).
- For 407, provide correct proxy credentials via the proxy URL (e.g., http://user:pass@proxy:port).
- For 502/503, verify the target host:port is reachable from the proxy.
- For 403, check proxy access control policies/firewall rules.
Example fix
// before (broken): no proxy credentials
os.Setenv("HTTPS_PROXY", "http://proxy.corp:3128")
// after (valid): include credentials
os.Setenv("HTTPS_PROXY", "http://user:pass@proxy.corp:3128") Defensive patterns
Strategy: fallback
Validate before calling
// Verify proxy credentials are set and target is reachable
func checkProxyConfig(proxyURL string) error {
u, err := url.Parse(proxyURL)
if err != nil { return err }
if u.User == nil {
return fmt.Errorf("proxy requires credentials but none provided")
}
return nil
} Try / catch
if strings.Contains(err.Error(), "connect handshake, status code") {
// proxy refused — likely auth or policy
// fall back to direct connection or alternate proxy
os.Unsetenv("HTTPS_PROXY")
conn, err = grpc.Dial(target, /* without proxy */)
} Prevention
- Ensure proxy credentials are correctly configured in HTTPS_PROXY or the proxy URL.
- Verify the target endpoint is allowed by proxy access policies.
- Have a fallback connection strategy (direct or alternate proxy) for proxy failures.
When it happens
Trigger: The proxy responded to CONNECT with a non-200 status (e.g., 407 Proxy Authentication Required, 502 Bad Gateway) and the response body could not be fully read/dumped. The status string (e.g., '407 Proxy Authentication Required') is the only diagnostic.
Common situations: Proxy requires authentication credentials that were not provided or are wrong (407), proxy cannot reach the upstream target (502/503), or proxy policy blocks the destination. The dump failure is usually because the connection was closed mid-body.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to do connect handshake, response: %q
- failed to write the HTTP request: %v
- reading server HTTP response: %v
- http status %d, body: %s
- empty accessToken in response (%v)
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/64443bd685d78381.
Report an issue: GitHub.