grpc/grpc-go · error
%v: %v
Error message
%v: %v
What it means
In the blocking DialContext loop (clientconn.go:317-330), a deferred handler fires when the dial context expires or is cancelled. If there is BOTH a context error and a connection error (and returnLastError is not the chosen branch), it returns fmt.Errorf("%v: %v", ctx.Err(), err), combining e.g. a deadline-exceeded with the underlying connection failure.
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 03255a9237)
Solutions
- Increase the dial context timeout or remove WithBlock if you don't need a synchronous ready.
- Address the underlying connection error shown after the colon (TLS, DNS, refused connection).
- Switch to grpc.NewClient which is non-blocking and reports failures per-RPC instead of at dial time.
Example fix
// before ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) conn, err := grpc.DialContext(ctx, target, grpc.WithBlock()) // after ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) conn, err := grpc.DialContext(ctx, target, grpc.WithBlock())
Defensive patterns
Strategy: try-catch
Validate before calling
// Size the context timeout to realistic connection latency before a blocking dial.
func dialTimeout(target string) time.Duration {
if strings.HasPrefix(target, "xds:///") { return 60 * time.Second }
return 10 * time.Second
} Try / catch
conn, err := grpc.DialContext(ctx, target, grpc.WithBlock())
if err != nil {
// err may combine ctx.Err() and connection error; inspect both
log.Printf("dial failed: %v (ctx: %v)", err, ctx.Err())
return nil, err
} Prevention
- Avoid WithBlock for long-lived channels; use NewClient and handle errors per-RPC.
- Set timeouts based on measured worst-case dial latency, not arbitrary small values.
- Separate deadline-exceeded from transport errors when logging.
When it happens
Trigger: Using grpc.DialContext with WithBlock(true) and a context that times out while the connection is also failing. The combined message surfaces when the context, not the transport, is the proximate cause.
Common situations: A short dial timeout against an unreachable backend; DNS resolution slower than the context deadline; the server rejecting TLS while the context expires simultaneously.
Related errors
- failed to exit idle mode: %w
- %s: %v
- failed to start resolver: %w
- could not get resolver for default scheme: %q
- ClientConn's authority from transport creds %q and dial opti
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/136bb3b8fd1e382e.
Report an issue: GitHub.