grpc/grpc-go · error

failed to do connect handshake, response: %q

Error message

failed to do connect handshake, response: %q

What it means

This error occurs in doHTTPConnectHandshake when the proxy returns a non-200 status to the CONNECT request and the full response was successfully dumped. The dumped response (headers + body) is included for diagnostics, showing exactly why the proxy refused the tunnel.

Source

Thrown at internal/transport/proxy.go:89

		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, err
	}
	return doHTTPConnectHandshake(ctx, conn, grpcUA, opts)

View on GitHub (pinned to 03255a9237)

Solutions

  1. Read the dumped response in the error message to identify the proxy's refusal reason.
  2. For 407 responses, provide valid proxy credentials in the HTTPS_PROXY URL.
  3. For 502/503, ensure the target endpoint is reachable from the proxy's network.
  4. For 403, verify the destination is allowed by proxy access control lists.

Example fix

// before (broken): missing credentials → 407 Proxy Authentication Required
os.Setenv("HTTPS_PROXY", "http://proxy:3128")

// after (valid): embedded credentials satisfy the 407 challenge
os.Setenv("HTTPS_PROXY", "http://svcuser:s3cret@proxy:3128")
Defensive patterns

Strategy: fallback

Try / catch

if strings.Contains(err.Error(), "connect handshake, response") {
    // parse the dumped response for status code
    // 407 → fix credentials; 502 → upstream issue; 403 → policy block
    log.Printf("proxy refused CONNECT: %v", err)
    // implement fallback to direct connection or alternate proxy
}

Prevention

When it happens

Trigger: The proxy responded to CONNECT with a non-200 status (e.g., 407, 502) and httputil.DumpResponse succeeded, so the complete HTTP response including headers and body is quoted in the error message.

Common situations: Proxy authentication failure (407 with Proxy-Authenticate header), upstream connectivity issue (502 with error page), proxy access policy rejection (403). Common in corporate environments with authenticated proxies.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/e7ad7d814faf663b. Report an issue: GitHub.