grpc/grpc-go · error
%v: %v
Error message
%v: %v
What it means
Inside DialContext's blocking wait loop, when the context expires while a connection error also exists and WithReturnConnectionError (returnLastError) is set, grpc-go combines both into "<context error>: <connection error>" (clientconn.go:325-326). This gives the caller both the deadline/timeout reason and the underlying transport failure.
Solutions
- Read both halves of the combined error: the context side (deadline exceeded / canceled) and the connection side (the real transport failure).
- Address the connection error (e.g., check host/port, TLS config, network reachability) and/or increase the context timeout.
- Drop WithBlock() if you do not need to block on READY; use grpc.NewClient and handle RPC errors instead.
Example fix
// before ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() cc, err := grpc.DialContext(ctx, addr, grpc.WithBlock(), grpc.WithReturnConnectionError()) // after: give more time and verify reachability ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
Defensive patterns
Strategy: try-catch
Try / catch
cc, err := grpc.DialContext(ctx, target, grpc.WithBlock(), grpc.WithReturnConnectionError())
if err != nil {
// err may be "<ctx error>: <conn error>"
if errors.Is(err, context.DeadlineExceeded) { /* timeout */ }
var connErr = ccErr // capture connectionError via separate path if needed
_ = connErr
} Prevention
- Set a realistic dial timeout for the network conditions.
- Verify host/port and TLS config before blocking on Dial.
- Prefer grpc.NewClient + handling RPC errors over blocking DialContext with WithReturnConnectionError.
When it happens
Trigger: grpc.Dial/DialContext is called with WithBlock(), WithReturnConnectionError(), and a context that times out before the channel reaches READY, while a connection error was recorded (e.g., all subchannels failed).
Common situations: Short dial timeouts against an unreachable backend; TLS handshake failures combined with a tight context deadline; wrong port or firewall dropping packets so the deadline fires first.
Related errors
- ClientConn's authority from transport creds
- could not get resolver for default scheme
- failed to exit idle mode
- failed to start resolver
- grpc: the provided default service config is invalid
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/136bb3b8fd1e382e.
Report an issue: GitHub.
Appendix: source
Thrown at clientconn.go:326
if !cc.dopts.block {
return cc, nil
}
if cc.dopts.timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)
defer cancel()
}
defer func() {
select {
case <-ctx.Done():
switch {
case ctx.Err() == err:
conn = nil
case err == nil || !cc.dopts.returnLastError:
conn, err = nil, ctx.Err()
default:
conn, err = nil, fmt.Errorf("%v: %v", ctx.Err(), err)
}
default:
}
}()
// A blocking dial blocks until the clientConn is ready.
for {
s := cc.GetState()
if s == connectivity.Idle {
cc.Connect()
}
if s == connectivity.Ready {
return cc, nil
} else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure {
if err = cc.connectionError(); err != nil {
terr, ok := err.(interface {
Temporary() bool
})View on GitHub (pinned to 0c51461d27)