grpc/grpc-go · error
failed to write the HTTP request
Error message
failed to write the HTTP request: %v
What it means
Fires in doHTTPConnectHandshake (proxy.go:75) when sendHTTPRequest fails to write the HTTP CONNECT request to the proxy connection. gRPC builds a CONNECT request (with optional Proxy-Authorization) and writes it to the TCP connection established to the proxy; if that write fails the handshake aborts and the connection is closed.
Solutions
- Verify the proxy address from HTTPS_PROXY/HTTP_PROXY (or grpc.WithContextDialer) resolves and accepts TCP connections.
- Check the proxy's logs/rules: it may be rejecting CONNECT to the target host or requiring authentication you didn't supply.
- Ensure the dial context has a generous-enough deadline; a too-short timeout can abort the write.
- If the proxy requires a TLS connection first, use an https:// proxy URL so Go dials TLS to the proxy before CONNECT.
Example fix
// before: proxy env points at a proxy that resets CONNECT
// HTTPS_PROXY=http://broken-proxy:3128
// after: correct, reachable proxy with auth
import (
_ "google.golang.org/grpc/credentials/insecure"
)
os.Setenv("HTTPS_PROXY", "http://user:pass@real-proxy:3128")
conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(creds)) Defensive patterns
Strategy: try-catch
Validate before calling
// Verify proxy reachability before dialing gRPC.
func proxyReachable(proxyURL string) error {
u, err := url.Parse(proxyURL)
if err != nil {
return err
}
c, err := net.DialTimeout("tcp", u.Host, 2*time.Second)
if err != nil {
return err
}
c.Close()
return nil
} Try / catch
conn, err := grpc.NewClient(target, opts...)
// proxy write failures surface during the first RPC or Dial.
// Wrap with a retry on transient network errors.
if err != nil {
if isTransient(err) {
// back off and retry NewClient / the RPC
}
} Prevention
- Validate HTTPS_PROXY/HTTP_PROXY at startup with a connectivity check.
- Give the dial context a generous deadline.
- Use https:// proxy URLs when the proxy requires TLS.
When it happens
Trigger: Setting HTTPS_PROXY/HTTP_PROXY (or WithContextDialer proxy config) and the write to the proxy socket fails. Causes: the proxy closed/reset the connection immediately after TCP connect, a network drop mid-write, a broken pipe because the proxy rejected the client, or the context was cancelled before/during the write.
Common situations: Misconfigured or unreachable HTTP proxy; proxy that immediately drops CONNECT to non-allowlisted hosts; transient network instability between client and proxy; cancelled/timed-out dial context; proxy requiring TLS but reached as plain TCP.
Related errors
- failed to do connect handshake, response
- failed to do connect handshake, status code
- reading server HTTP response
- ErrCodeEnhanceYourCalm
- gRPC requires HTTP/2
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/e2486f793d282648.
Report an issue: GitHub.
Appendix: 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 willView on GitHub (pinned to 0c51461d27)