geektutu/7days-golang · error
rpc client: call failed:
Error message
rpc client: call failed:
What it means
Client.Call starts an asynchronous call via Client.Go and then blocks in a select. If the caller-supplied context is cancelled or its deadline expires before the response arrives, Call removes the pending call from the client's pending map and returns this error wrapping ctx.Err(). The library throws it so that context cancellation (timeout or manual cancel) is surfaced as the call result instead of blocking forever.
Source
Thrown at gee-rpc/day4-timeout/client.go:188
}
call := &Call{
ServiceMethod: serviceMethod,
Args: args,
Reply: reply,
Done: done,
}
client.send(call)
return call
}
// Call invokes the named function, waits for it to complete,
// and returns its error status.
func (client *Client) Call(ctx context.Context, serviceMethod string, args, reply interface{}) error {
call := client.Go(serviceMethod, args, reply, make(chan *Call, 1))
select {
case <-ctx.Done():
client.removeCall(call.Seq)
return errors.New("rpc client: call failed: " + ctx.Err().Error())
case call := <-call.Done:
return call.Error
}
}
func parseOptions(opts ...*Option) (*Option, error) {
// if opts is nil or pass nil as parameter
if len(opts) == 0 || opts[0] == nil {
return DefaultOption, nil
}
if len(opts) != 1 {
return nil, errors.New("number of options is more than 1")
}
opt := opts[0]
opt.MagicNumber = DefaultOption.MagicNumber
if opt.CodecType == "" {
opt.CodecType = DefaultOption.CodecType
}View on GitHub (pinned to cf36443821)
Solutions
- Increase the context deadline (e.g. context.WithTimeout(ctx, 5*time.Second)) to a value that accommodates the slowest server method
- Investigate why the server is slow: profile the handler, check server-side blocking or long DB queries
- Handle the timeout explicitly: check errors.Is / strings.HasPrefix against "rpc client: call failed: context deadline exceeded" and retry with backoff
- Pass context.Background() if the call genuinely has no time limit (use sparingly)
Example fix
// before ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() err := client.Call(ctx, "Foo.Sum", req, reply) // times out on slow handlers // after ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() err := client.Call(ctx, "Foo.Sum", req, reply)
Defensive patterns
Strategy: try-catch
Validate before calling
// before calling, ensure the budget is sane
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < 100*time.Millisecond {
return fmt.Errorf("call budget too small: %v", time.Until(deadline))
} Try / catch
err := client.Call(ctx, "Foo.Sum", args, reply)
if err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
// timeout path: retry with backoff or fail fast
} else if errors.Is(ctx.Err(), context.Canceled) {
// caller cancelled: propagate
}
} Prevention
- Set per-RPC timeouts proportionate to the handler's worst-case latency
- Use context.WithTimeoutCause (or wrap) to attach context to timeouts
- Add server-side deadlines/metrics to find slow handlers
- Retry idempotent calls on deadline exceeded with exponential backoff
When it happens
Trigger: Calling Call(ctx, ...) where ctx is a context.WithTimeout/WithDeadline that expires before the server replies, or a cancellable context that is cancelled by another goroutine while the RPC is in flight.
Common situations: Server handler is slow or hung and the client-side timeout fires; network stall to the server; caller cancels the request context because an upstream HTTP request was aborted; timeout set too aggressively for a heavy method.
Related errors
- rpc client: call failed:
- rpc client: call failed:
- rpc client: call failed:
- number of options is more than 1
- rpc server: service/method request ill-formed:
AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03).
Data as JSON: /api/errors/1acc394ecd4e7b2f.
Report an issue: GitHub.