{"record":{"id":"d5760150a7962906","repo":"grpc/grpc-go","slug":"grpc-the-connection-is-closing-due-to-channel-idl","errorCode":null,"errorMessage":"grpc: the connection is closing due to channel idleness","messagePattern":"grpc: the connection is closing due to channel idleness","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"clientconn.go","lineNumber":77,"sourceCode":"const (\n\t// minimum time to give a connection to complete\n\tminConnectTimeout = 20 * time.Second\n)\n\nvar (\n\t// ErrClientConnClosing indicates that the operation is illegal because\n\t// the ClientConn is closing.\n\t//\n\t// Deprecated: this error should not be relied upon by users; use the status\n\t// code of Canceled instead.\n\tErrClientConnClosing = status.Error(codes.Canceled, \"grpc: the client connection is closing\")\n\t// errConnDrain indicates that the connection starts to be drained and does not accept any new RPCs.\n\terrConnDrain = errors.New(\"grpc: the connection is drained\")\n\t// errConnClosing indicates that the connection is closing.\n\terrConnClosing = errors.New(\"grpc: the connection is closing\")\n\t// errConnIdling indicates the connection is being closed as the channel\n\t// is moving to an idle mode due to inactivity.\n\terrConnIdling = errors.New(\"grpc: the connection is closing due to channel idleness\")\n\t// invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default\n\t// service config.\n\tinvalidDefaultServiceConfigErrPrefix = \"grpc: the provided default service config is invalid\"\n\t// PickFirstBalancerName is the name of the pick_first balancer.\n\tPickFirstBalancerName = pickfirst.Name\n)\n\n// The following errors are returned from Dial and DialContext\nvar (\n\t// errNoTransportSecurity indicates that there is no transport security\n\t// being set for ClientConn. Users should either set one or explicitly\n\t// call WithInsecure DialOption to disable security.\n\terrNoTransportSecurity = errors.New(\"grpc: no transport security set (use grpc.WithTransportCredentials(insecure.NewCredentials()) explicitly or set credentials)\")\n\t// errTransportCredsAndBundle indicates that creds bundle is used together\n\t// with other individual Transport Credentials.\n\terrTransportCredsAndBundle = errors.New(\"grpc: credentials.Bundle may not be used with individual TransportCredentials\")\n\t// errNoTransportCredsInBundle indicated that the configured creds bundle\n\t// returned a transport credentials which was nil.","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/clientconn.go#L59-L95","documentation":"Internal sentinel errConnIdling (clientconn.go:75-77) passed to tearDown when the channel moves into IDLE after the idle timeout. In enterIdleMode (clientconn.go:463-466) every addrConn is torn down with errConnIdling, closing the transport without failing already-started RPCs. New RPCs or an explicit Connect() exit idle.","triggerScenarios":"The channel's idle timer fires (default WithIdleTimeout — 30m in recent versions, 0 disables) because no RPCs were issued for the period; the idleness manager calls enterIdleMode which tears subchannels down with errConnIdling. A subsequent pick against a stale SubConn surfaces it.","commonSituations":"Low-traffic services that go quiet long enough to hit the idle timeout; default idle timeout enabled unexpectedly after a grpc-go upgrade; observability picking up the teardown as an error; bursts after quiet periods.","solutions":["Call cc.Connect() to force the channel out of IDLE before issuing RPCs, or issue RPCs with grpc.WaitForReady(true) so they block through reconnection.","Tune the idle timeout via grpc.WithIdleTimeout(d) — set it longer or to 0 to disable idling entirely if the reconnection cost is unacceptable.","Keep the channel warm with a lightweight health-check RPC if you must avoid idle transitions.","Distinguish errConnIdling from a real failure: it is expected behavior, not a backend problem."],"exampleFix":"// before — default idle timeout fires, first RPC after quiet period stalls/fails\ncc, _ := grpc.NewClient(target)\n// ... 35 minutes pass ...\nclient.SayHello(ctx, req) // channel was idling\n\n// after — disable idle, or explicitly reconnect\ncc, _ := grpc.NewClient(target, grpc.WithIdleTimeout(0)) // never idle\n// or\ncc.Connect()\nclient.SayHello(ctx, req, grpc.WaitForReady(true))","handlingStrategy":"retry","validationCode":"// Force the channel out of idle before issuing RPCs\ncc.Connect() // exits IDLE if necessary\nclient.SayHello(ctx, req, grpc.WaitForReady(true))","typeGuard":null,"tryCatchPattern":"if status.Code(err) == codes.Unavailable && /* idling */ cc.GetState() == connectivity.Idle {\n    cc.Connect(); /* retry */\n}","preventionTips":["Tune grpc.WithIdleTimeout for your traffic pattern (0 disables).","Call cc.Connect() proactively on quiet channels.","Use WaitForReady(true) after quiet periods."],"tags":["connection","idle","lifecycle","grpc-go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}