grpc/grpc-go · error
grpc: the connection is closing
Error message
grpc: the connection is closing
What it means
Internal sentinel errConnClosing (clientconn.go:73-74) surfaced when the ClientConn is being torn down or the dial context is cancelled. It is returned from exitIdleMode when cc.conns==nil (clientconn.go:394-396) and from tryAllAddrs when ctx.Err()!=nil (clientconn.go:1455-1456). The user-facing analogue is ErrClientConnClosing (status Canceled).
Source
Thrown at clientconn.go:74
_ "google.golang.org/grpc/resolver/dns" // To register dns resolver.
)
const (
// minimum time to give a connection to complete
minConnectTimeout = 20 * time.Second
)
var (
// ErrClientConnClosing indicates that the operation is illegal because
// the ClientConn is closing.
//
// Deprecated: this error should not be relied upon by users; use the status
// code of Canceled instead.
ErrClientConnClosing = status.Error(codes.Canceled, "grpc: the client connection is closing")
// errConnDrain indicates that the connection starts to be drained and does not accept any new RPCs.
errConnDrain = errors.New("grpc: the connection is drained")
// errConnClosing indicates that the connection is closing.
errConnClosing = errors.New("grpc: the connection is closing")
// errConnIdling indicates the connection is being closed as the channel
// is moving to an idle mode due to inactivity.
errConnIdling = errors.New("grpc: the connection is closing due to channel idleness")
// invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default
// service config.
invalidDefaultServiceConfigErrPrefix = "grpc: the provided default service config is invalid"
// PickFirstBalancerName is the name of the pick_first balancer.
PickFirstBalancerName = pickfirst.Name
)
// The following errors are returned from Dial and DialContext
var (
// errNoTransportSecurity indicates that there is no transport security
// being set for ClientConn. Users should either set one or explicitly
// call WithInsecure DialOption to disable security.
errNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithTransportCredentials(insecure.NewCredentials()) explicitly or set credentials)")
// errTransportCredsAndBundle indicates that creds bundle is used together
// with other individual Transport Credentials.View on GitHub (pinned to 03255a9237)
Solutions
- Stop issuing RPCs on a ClientConn before/after Close(); treat Close as terminal — create a new ClientConn to reconnect.
- If you need ordered shutdown, wait for in-flight RPCs to finish (WaitGroup/context) before Close().
- When using DialContext, ensure the context outlives the dial (or pass context.Background() and rely on WithTimeout for the connect deadline).
- Map Canceled status (ErrClientConnClosing) at the application layer to a clean shutdown rather than a retryable error.
Example fix
// before — reuse after close cc, _ := grpc.NewClient(target) cc.Close() client.SayHello(ctx, req) // -> connection is closing // after — create a fresh client to reconnect cc, _ := grpc.NewClient(target) // ... use cc ... cc.Close() cc2, _ := grpc.NewClient(target) defer cc2.Close()
Defensive patterns
Strategy: validation
Validate before calling
// Treat Close as terminal; do not reuse the ClientConn
func dialOnce(target string) *grpc.ClientConn {
cc, err := grpc.NewClient(target, /* creds */)
if err != nil { panic(err) }
return cc // caller owns Close(); never use after Close()
} Try / catch
if errors.Is(err, grpc.ErrClientConnClosing) || status.Code(err) == codes.Canceled {
// channel is shutting down; do not retry on the same ClientConn
} Prevention
- Never issue RPCs after Close(); create a new ClientConn to reconnect.
- Wait for in-flight RPCs (WaitGroup) before Close().
- Pass a long-lived context to DialContext; cancel only for real abort.
When it happens
Trigger: ClientConn.Close() was called (or is in progress) while an RPC or connection attempt is outstanding; the dial context was cancelled before a transport was established; the channel is re-entering idle while a connection attempt races.
Common situations: Calling Close() then reusing the same *grpc.ClientConn; cancelling the DialContext; application shutdown racing with in-flight RPCs; defer-ordered teardown that closes the channel before pending calls return.
Related errors
- grpc: the connection is closing due to channel idleness
- no SubConn is available
- grpc: the connection is drained
- all SubConns are in TransientFailure
- bad resolver state
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/97225e157f834de3.
Report an issue: GitHub.