grpc/grpc-go · error

failed to write the HTTP request: %v

Error message

failed to write the HTTP request: %v

What it means

This error occurs in doHTTPConnectHandshake when sendHTTPRequest fails to write the HTTP CONNECT request to the proxy connection. The underlying conn.Write returned an error, indicating the TCP connection to the proxy was broken or the write was interrupted.

Source

Thrown at internal/transport/proxy.go:75

func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, grpcUA string, opts proxyattributes.Options) (_ net.Conn, err error) {
	defer func() {
		if err != nil {
			conn.Close()
		}
	}()

	req := &http.Request{
		Method: http.MethodConnect,
		URL:    &url.URL{Host: opts.ConnectAddr},
		Header: map[string][]string{"User-Agent": {grpcUA}},
	}
	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

View on GitHub (pinned to 03255a9237)

Solutions

  1. Verify the proxy address is reachable and the proxy process is running.
  2. Check network connectivity/firewall rules between the client and the proxy host:port.
  3. Retry the connection (transient proxy/network issues often resolve on retry).
  4. If the context deadline is too short, increase the dial timeout with grpc.WithTransportCredentials + context.WithTimeout.

Example fix

// before (broken): no retry on transient proxy write failure
conn, err := grpc.Dial(target, grpc.WithBlock())

// after (valid): use a dial timeout and retry/backoff policy
conn, err := grpc.Dial(target,
  grpc.WithConnectParams(grpc.ConnectParams{
    Backoff:           backoff.Config{BaseDelay: 1*time.Second, MaxDelay: 10*time.Second},
    MinConnectTimeout: 5 * time.Second,
  }),
)
Defensive patterns

Strategy: retry

Try / catch

// gRPC client connection with retry/backoff handles transient proxy write failures
conn, err := grpc.Dial(target,
    grpc.WithConnectParams(grpc.ConnectParams{
        Backoff:           backoff.Config{BaseDelay: 1*time.Second, MaxDelay: 30*time.Second, Multiplier: 1.6},
        MinConnectTimeout: 5 * time.Second,
    }),
)
if err != nil {
    log.Printf("failed to connect via proxy: %v", err)
}

Prevention

When it happens

Trigger: Calling grpc.Dial with a proxy configured (HTTPS_PROXY/HTTPS_PROXY env var or WithContextDialer proxy), and the TCP connection to the proxy drops before or during the CONNECT request write — e.g., proxy process crashed, network partition, or the dial context was cancelled.

Common situations: Corporate proxy restarts/failures, flaky network between client and forward proxy, proxy rejecting connections at the TCP level (connection reset), or context cancellation (deadline exceeded) during proxy handshake.

Related errors


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