{"record":{"id":"669d831492e41b82","repo":"geektutu/7days-golang","slug":"rpc-client-connect-timeout-expect-within-s-669d83","errorCode":null,"errorMessage":"rpc client: connect timeout: expect within %s","messagePattern":"rpc client: connect timeout: expect within (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-rpc/day5-http-debug/client.go","lineNumber":273,"sourceCode":"\t}\n\t// close the connection if client is nil\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = conn.Close()\n\t\t}\n\t}()\n\tch := make(chan clientResult)\n\tgo func() {\n\t\tclient, err := f(conn, opt)\n\t\tch <- clientResult{client: client, err: err}\n\t}()\n\tif opt.ConnectTimeout == 0 {\n\t\tresult := <-ch\n\t\treturn result.client, result.err\n\t}\n\tselect {\n\tcase <-time.After(opt.ConnectTimeout):\n\t\treturn nil, fmt.Errorf(\"rpc client: connect timeout: expect within %s\", opt.ConnectTimeout)\n\tcase result := <-ch:\n\t\treturn result.client, result.err\n\t}\n}\n\n// Dial connects to an RPC server at the specified network address\nfunc Dial(network, address string, opts ...*Option) (*Client, error) {\n\treturn dialTimeout(NewClient, network, address, opts...)\n}\n\n// NewHTTPClient new a Client instance via HTTP as transport protocol\nfunc NewHTTPClient(conn net.Conn, opt *Option) (*Client, error) {\n\t_, _ = io.WriteString(conn, fmt.Sprintf(\"CONNECT %s HTTP/1.0\\n\\n\", defaultRPCPath))\n\n\t// Require successful HTTP response\n\t// before switching to RPC protocol.\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: \"CONNECT\"})\n\tif err == nil && resp.Status == connected {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-rpc/day5-http-debug/client.go#L255-L291","documentation":"dialTimeout in gee-rpc/day5-http-debug enforces opt.ConnectTimeout on connection setup; exceeding it returns \"rpc client: connect timeout: expect within %s\". Unlike day4 it is also on the DialHTTP path, so HTTP-tunneled RPC clients face the same deadline.","triggerScenarios":"Dial or DialHTTP with ConnectTimeout > 0 while the TCP connect plus options exchange (or the HTTP CONNECT-style handshake for DialHTTP) takes longer than the deadline; unreachable host or server that accepts but never answers.","commonSituations":"Reverse proxy or load balancer in front of the RPC server adding latency; server hung (accepting sockets but not reading); aggressive ConnectTimeout in ephemeral CI environments; DNS resolution delays.","solutions":["Raise opt.ConnectTimeout (e.g. 3-5s) to cover DNS + connect + options exchange","Confirm the server/proxy is up and the port is reachable before dialing","Set ConnectTimeout to 0 to disable the client-side deadline","Add retry with backoff around Dial for transient latency spikes"],"exampleFix":"// before\nopt := &geerpc.Option{ConnectTimeout: 50 * time.Millisecond}\nclient, err := geerpc.DialHTTP(\"tcp\", \"api.example.com/rpc\", opt)\n// after\nopt := &geerpc.Option{ConnectTimeout: 5 * time.Second}\nclient, err := geerpc.DialHTTP(\"tcp\", \"api.example.com/rpc\", opt)","handlingStrategy":"retry","validationCode":"if opt.ConnectTimeout != 0 && opt.ConnectTimeout < 500*time.Millisecond {\n\treturn errors.New(\"ConnectTimeout too aggressive for HTTP tunneled dial\")\n}","typeGuard":null,"tryCatchPattern":"client, err := geerpc.DialHTTP(\"tcp\", addr, opt)\nif err != nil && strings.Contains(err.Error(), \"connect timeout\") {\n\ttime.Sleep(time.Second)\n\tclient, err = geerpc.DialHTTP(\"tcp\", addr, opt)\n}","preventionTips":["Allow extra ConnectTimeout headroom when proxies sit between client and server","Health-check the HTTP endpoint before dialing","Prefer 0 (no deadline) in flaky network environments","Wrap DialHTTP with bounded retries and backoff"],"tags":["rpc","network","timeout","http","dial"],"backgroundTag":"connect-timeout","analyzedSha":"cf3644382101dc13e7fd92e8f5c66cabc51bcd3b","analyzedAt":"2026-09-03T18:31:24.087Z","contentChangedAt":"2026-09-03T18:31:24.087Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}