grpc/grpc-go · info
grpc: timed out when dialing
Error message
grpc: timed out when dialing
What it means
ErrClientConnTimeout (clientconn.go:1784-1789) is a DEPRECATED exported sentinel whose doc comment states plainly: 'This error is never returned by grpc and should not be referenced by users.' Modern grpc-go surfaces dial timeouts as context.DeadlineExceeded (from the dial context) wrapped in a status with code Unavailable.
Source
Thrown at clientconn.go:1789
func (ac *addrConn) incrCallsStarted() {
ac.channelz.ChannelMetrics.CallsStarted.Add(1)
ac.channelz.ChannelMetrics.LastCallStartedTimestamp.Store(time.Now().UnixNano())
}
func (ac *addrConn) incrCallsSucceeded() {
ac.channelz.ChannelMetrics.CallsSucceeded.Add(1)
}
func (ac *addrConn) incrCallsFailed() {
ac.channelz.ChannelMetrics.CallsFailed.Add(1)
}
// ErrClientConnTimeout indicates that the ClientConn cannot establish the
// underlying connections within the specified timeout.
//
// Deprecated: This error is never returned by grpc and should not be
// referenced by users.
var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
// getResolver finds the scheme in the cc's resolvers or the global registry.
// scheme should always be lowercase (typically by virtue of url.Parse()
// performing proper RFC3986 behavior).
func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
for _, rb := range cc.dopts.resolvers {
if scheme == rb.Scheme() {
return rb
}
}
return resolver.Get(scheme)
}
func (cc *ClientConn) updateConnectionError(err error) {
cc.lceMu.Lock()
cc.lastConnectionError = err
cc.lceMu.Unlock()
}View on GitHub (pinned to 03255a9237)
Solutions
- Stop comparing against ErrClientConnTimeout — it is never produced by the library.
- Detect dial timeout via the error returned from DialContext/Invoke: check status.Code(err)==codes.Unavailable and/or errors.Is(err, context.DeadlineExceeded).
- Use grpc.WithTimeout (deprecated) or, preferably, a context.WithTimeout passed to the call to bound dial/RPC time.
- Inspect connectionError() / the wrapped cause for the real TCP/TLS failure underneath the timeout.
Example fix
// before — deprecated, never matches
if err == grpc.ErrClientConnTimeout { ... }
// after — use status + context
st, ok := status.FromError(err)
if ok && st.Code() == codes.Unavailable && errors.Is(err, context.DeadlineExceeded) {
// dial/RPC timed out
} Defensive patterns
Strategy: try-catch
Validate before calling
// Do NOT reference ErrClientConnTimeout; detect timeouts via status+context
func isDialTimeout(err error) bool {
return status.Code(err) == codes.Unavailable && errors.Is(err, context.DeadlineExceeded)
} Try / catch
if errors.Is(err, context.DeadlineExceeded) || status.Code(err) == codes.Unavailable {
// dial/RPC timed out; ErrClientConnTimeout is never returned by grpc
} Prevention
- Remove any `err == grpc.ErrClientConnTimeout` checks; it never matches.
- Bound dial time with context.WithTimeout on the call.
- Inspect wrapped errors via errors.Is rather than equality.
When it happens
Trigger: Historically meant to indicate the connection could not be established within the timeout. In current grpc-go nothing returns it; the only way a user sees it is by importing and comparing against it themselves. Real dial timeouts come through as the context error / a status.Unavailable wrapping the underlying connection error.
Common situations: Legacy code that does if err == grpc.ErrClientConnTimeout; copying old examples; static analysis flagging the symbol; grepping for timeout handling.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- all SubConns are in TransientFailure
- %v: %v
- malformed grpc-timeout: %v
- no SubConn is available
- bad resolver state
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/9fb4c429a54d89ec.
Report an issue: GitHub.