{"record":{"id":"20671c55a2943e52","repo":"grpc/grpc-go","slug":"reading-server-http-response-v","errorCode":null,"errorMessage":"reading server HTTP response: %v","messagePattern":"reading server HTTP response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/proxy.go","lineNumber":81,"sourceCode":"\n\treq := &http.Request{\n\t\tMethod: http.MethodConnect,\n\t\tURL:    &url.URL{Host: opts.ConnectAddr},\n\t\tHeader: map[string][]string{\"User-Agent\": {grpcUA}},\n\t}\n\tif user := opts.User; user != nil {\n\t\tu := user.Username()\n\t\tp, _ := user.Password()\n\t\treq.Header.Add(proxyAuthHeaderKey, \"Basic \"+basicAuth(u, p))\n\t}\n\tif err := sendHTTPRequest(ctx, req, conn); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to write the HTTP request: %v\", err)\n\t}\n\n\tr := bufio.NewReader(conn)\n\tresp, err := http.ReadResponse(r, req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading server HTTP response: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tdump, err := httputil.DumpResponse(resp, true)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to do connect handshake, status code: %s\", resp.Status)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to do connect handshake, response: %q\", dump)\n\t}\n\t// The buffer could contain extra bytes from the target server, so we can't\n\t// discard it. However, in many cases where the server waits for the client\n\t// to send the first message (e.g. when TLS is being used), the buffer will\n\t// be empty, so we can avoid the overhead of reading through this buffer.\n\tif r.Buffered() != 0 {\n\t\treturn &bufConn{Conn: conn, r: r}, nil\n\t}\n\treturn conn, nil\n}","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/transport/proxy.go#L63-L99","documentation":"This error occurs in doHTTPConnectHandshake when http.ReadResponse fails to read the proxy's response to the CONNECT request. The bufio.Reader could not parse a valid HTTP response from the proxy — the connection may have closed, timed out, or the proxy sent a non-HTTP response.","triggerScenarios":"After successfully writing the CONNECT request to the proxy, reading the response fails. This happens when the proxy closes the connection abruptly, sends garbage/non-HTTP data, or the connection times out before a full response is received.","commonSituations":"Proxy is overloaded and drops connections mid-response, proxy is not actually an HTTP proxy (e.g., pointing at a plain TCP service), TLS-intercepting proxy that resets plain CONNECT, or the proxy returns a response larger than the read buffer under network congestion.","solutions":["Confirm the proxy address is an HTTP/HTTPS proxy that supports CONNECT tunneling (not a SOCKS or raw TCP service).","Check proxy logs for connection drops or errors on the target port.","Test the proxy manually: curl -x http://proxy:port https://target to isolate gRPC vs proxy issues.","Retry with backoff if the proxy is intermittently unreliable."],"exampleFix":"// before (broken): pointing at a non-HTTP proxy port\nos.Setenv(\"HTTPS_PROXY\", \"http://10.0.0.1:9999\") // raw TCP service\n\n// after (valid): correct HTTP proxy address\nos.Setenv(\"HTTPS_PROXY\", \"http://10.0.0.1:3128\") // squid/tinyproxy HTTP CONNECT","handlingStrategy":"retry","validationCode":"// Verify the proxy supports HTTP CONNECT before using it\nfunc testProxyConnect(proxyURL, targetHost string) error {\n    u, err := url.Parse(proxyURL)\n    if err != nil { return err }\n    conn, err := net.DialTimeout(\"tcp\", u.Host, 5*time.Second)\n    if err != nil { return err }\n    defer conn.Close()\n    req := fmt.Sprintf(\"CONNECT %s HTTP/1.1\\r\\nHost: %s\\r\\n\\r\\n\", targetHost, targetHost)\n    _, err = conn.Write([]byte(req))\n    return err\n}","typeGuard":null,"tryCatchPattern":"conn, err := grpc.Dial(target,\n    grpc.WithConnectParams(grpc.ConnectParams{\n        Backoff: backoff.Config{BaseDelay: 2*time.Second, MaxDelay: 30*time.Second},\n        MinConnectTimeout: 10 * time.Second,\n    }),\n)\nif err != nil {\n    // check if it's a proxy response read failure\n    if strings.Contains(err.Error(), \"reading server HTTP response\") {\n        log.Printf(\"proxy may not support CONNECT or is unhealthy\")\n    }\n}","preventionTips":["Verify the proxy address points to an HTTP proxy, not a raw TCP service.","Test proxy connectivity independently (curl -x) before deploying gRPC clients.","Configure backoff/retry to handle transient proxy issues."],"tags":["transport","proxy","http-connect","network","response-parsing"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}