{"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":"http","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/0c51461d27177d997e14c642fe18c11668fc09a3/internal/transport/proxy.go#L63-L99","documentation":"Fires in doHTTPConnectHandshake (proxy.go:81) when http.ReadResponse cannot parse the proxy's reply to the CONNECT request. After writing CONNECT, gRPC reads the HTTP/1.1 response status line and headers from the proxy via a bufio.Reader; if the bytes received are not a valid HTTP response (or the read errors), this wraps the cause.","triggerScenarios":"The proxy sends non-HTTP bytes, closes the connection before a full response, or the read errors (reset, timeout, EOF). Examples: a proxy that speaks TLS-first but was reached as plain TCP and sends a ServerHello; a transparent proxy returning raw bytes; the connection dropping mid-response; context cancellation during the read.","commonSituations":"Pointing HTTPS_PROXY at a TLS-terminating proxy over plain TCP (scheme mismatch); a proxy that closes on unsupported methods; network interference (middleboxes, captive portals injecting HTML); cancelled dial context; proxy returning a malformed status line.","solutions":["Confirm the proxy URL scheme matches what the proxy expects (https:// if the proxy itself requires TLS).","Reproduce with curl --proxy <url> <target> to see the raw response; if curl fails similarly, the proxy is the issue.","Increase the dial/read context deadline; a tight timeout can truncate the response read.","Bypass the proxy temporarily to confirm the target is reachable, then fix the proxy configuration."],"exampleFix":"// before: TLS-expecting proxy reached as plain HTTP\n//   HTTPS_PROXY=http://tls-proxy:443\n\n// after\nos.Setenv(\"HTTPS_PROXY\", \"https://tls-proxy:443\")\n// Go will dial TLS to the proxy, then issue CONNECT.","handlingStrategy":"try-catch","validationCode":"// Smoke-test the proxy with curl-like CONNECT before relying on it.\nfunc probeProxyConnect(ctx context.Context, proxyURL, target string) error {\n    // open TCP to proxy, write CONNECT target, read status line\n    // return error if not HTTP/1.x\n    return nil\n}","typeGuard":null,"tryCatchPattern":"conn, err := grpc.NewClient(target, opts...)\nif err != nil && strings.Contains(err.Error(), \"reading server HTTP response\") {\n    // likely a scheme mismatch (http vs https) with the proxy\n}","preventionTips":["Match the proxy URL scheme to the proxy's transport (http:// vs https://).","Reproduce proxy issues with curl --proxy before debugging gRPC.","Set a generous dial deadline."],"tags":["proxy","network","http","connect","transport"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}