{"record":{"id":"64443bd685d78381","repo":"grpc/grpc-go","slug":"failed-to-do-connect-handshake-status-code-s","errorCode":null,"errorMessage":"failed to do connect handshake, status code: %s","messagePattern":"failed to do connect handshake, status code: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/proxy.go","lineNumber":87,"sourceCode":"\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}\n\n// proxyDial establishes a TCP connection to the specified address and performs an HTTP CONNECT handshake.\nfunc proxyDial(ctx context.Context, addr resolver.Address, grpcUA string, opts proxyattributes.Options) (net.Conn, error) {\n\tconn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, \"tcp\", addr.Addr)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/transport/proxy.go#L69-L105","documentation":"This error occurs in doHTTPConnectHandshake when the proxy returns a non-200 status to the CONNECT request AND httputil.DumpResponse also fails (so only the raw status string is included). This means the proxy explicitly refused the tunnel and the response body could not be dumped for diagnostics.","triggerScenarios":"The proxy responded to CONNECT with a non-200 status (e.g., 407 Proxy Authentication Required, 502 Bad Gateway) and the response body could not be fully read/dumped. The status string (e.g., '407 Proxy Authentication Required') is the only diagnostic.","commonSituations":"Proxy requires authentication credentials that were not provided or are wrong (407), proxy cannot reach the upstream target (502/503), or proxy policy blocks the destination. The dump failure is usually because the connection was closed mid-body.","solutions":["Check the HTTP status code in the error to determine the proxy's refusal reason (407 = auth, 502 = upstream failure, 403 = policy).","For 407, provide correct proxy credentials via the proxy URL (e.g., http://user:pass@proxy:port).","For 502/503, verify the target host:port is reachable from the proxy.","For 403, check proxy access control policies/firewall rules."],"exampleFix":"// before (broken): no proxy credentials\nos.Setenv(\"HTTPS_PROXY\", \"http://proxy.corp:3128\")\n\n// after (valid): include credentials\nos.Setenv(\"HTTPS_PROXY\", \"http://user:pass@proxy.corp:3128\")","handlingStrategy":"fallback","validationCode":"// Verify proxy credentials are set and target is reachable\nfunc checkProxyConfig(proxyURL string) error {\n    u, err := url.Parse(proxyURL)\n    if err != nil { return err }\n    if u.User == nil {\n        return fmt.Errorf(\"proxy requires credentials but none provided\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"connect handshake, status code\") {\n    // proxy refused — likely auth or policy\n    // fall back to direct connection or alternate proxy\n    os.Unsetenv(\"HTTPS_PROXY\")\n    conn, err = grpc.Dial(target, /* without proxy */)\n}","preventionTips":["Ensure proxy credentials are correctly configured in HTTPS_PROXY or the proxy URL.","Verify the target endpoint is allowed by proxy access policies.","Have a fallback connection strategy (direct or alternate proxy) for proxy failures."],"tags":["transport","proxy","http-connect","authentication","network"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}