geektutu/7days-golang · error
%s
Error message
%s
What it means
In gee-rpc/day4-timeout's receive loop, a failure reading the response body sets the Call's Error to "reading body <cause>" and terminates the connection and every pending call. The timeout additions on the client do not change this path: it is still a broken/undecodable response stream.
Source
Thrown at gee-rpc/day4-timeout/client.go:148
}
}
}
func (client *Client) receive() {
var err error
for err == nil {
var h codec.Header
if err = client.cc.ReadHeader(&h); err != nil {
break
}
call := client.removeCall(h.Seq)
switch {
case call == nil:
// it usually means that Write partially failed
// and call was already removed.
err = client.cc.ReadBody(nil)
case h.Error != "":
call.Error = fmt.Errorf(h.Error)
err = client.cc.ReadBody(nil)
call.done()
default:
err = client.cc.ReadBody(call.Reply)
if err != nil {
call.Error = errors.New("reading body " + err.Error())
}
call.done()
}
}
// error occurs, so terminateCalls pending calls
client.terminateCalls(err)
}
// Go invokes the function asynchronously.
// It returns the Call structure representing the invocation.
func (client *Client) Go(serviceMethod string, args, reply interface{}, done chan *Call) *Call {
if done == nil {View on GitHub (pinned to cf36443821)
Solutions
- Align ConnectTimeout/HandleTimeout on the server and client so neither side drops the connection mid-response
- Pass a correct non-nil pointer reply matching the server's reply type
- Check server logs for panics or forced connection closes
- Retry with a fresh Dial after this error; the old client is unusable
Example fix
// before
client, _ := geerpc.Dial("tcp", addr, opt) // opt timeouts too small
err := client.Call("Foo.Sum", req, reply)
// after
opt := &geerpc.Option{ConnectTimeout: 5 * time.Second, HandleTimeout: 10 * time.Second}
client, err := geerpc.Dial("tcp", addr, opt)
err = client.Call("Foo.Sum", req, &reply) Defensive patterns
Strategy: retry
Validate before calling
opt := &geerpc.Option{ConnectTimeout: 5 * time.Second, HandleTimeout: 10 * time.Second}
if opt.HandleTimeout <= opt.ConnectTimeout { /* server may close mid-response; adjust */ } Type guard
func replyIsPointer(v interface{}) bool {
rv := reflect.ValueOf(v)
return rv.IsValid() && rv.Kind() == reflect.Ptr && !rv.IsNil()
} Try / catch
if err := client.Call("Foo.Sum", req, reply); err != nil {
if strings.HasPrefix(err.Error(), "reading body") {
client, err = geerpc.Dial("tcp", addr, opt)
if err == nil { err = client.Call("Foo.Sum", req, reply) }
}
} Prevention
- Set HandleTimeout generously larger than ConnectTimeout and expected method latency
- Always pass a pointer reply of the exact server type
- Check server logs for panics or forced closes after timeouts
- Treat this error as fatal to the connection: dial a fresh client before retrying
When it happens
Trigger: A Call/Go on a day4-timeout client gets a header but ReadBody fails: server closed the connection after the header, response truncated (possibly by a timeout handler closing the conn), or reply decode type mismatch.
Common situations: Server-side handler timeout closes the socket mid-response; client-side Call timeout abandons the call while the server is still writing; reply pointer type mismatch; server panic during serialization.
Related errors
- %s
- %s
- rpc client: connect timeout: expect within %s
- rpc client: connect timeout: expect within %s
- rpc client: connect timeout: expect within %s
AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03).
Data as JSON: /api/errors/b1d296d81eedfb0e.
Report an issue: GitHub.