grpc/grpc-go · error
grpc: the connection is closing
Error message
grpc: the connection is closing
What it means
Raised by grpc-go's client connection machinery (clientconn.go) as errConnClosing = errors.New("grpc: the connection is closing"). It means the HTTP/2 transport underlying a ClientConn is being torn down, so an RPC attempt (or other per-connection operation) is rejected because the connection is closing and cannot accept or continue work. Note the related, deprecated ErrClientConnClosing (status code Canceled, "grpc: the client connection is closing"): grpc-go's own guidance is to stop matching on these sentinel errors and instead check for status code Canceled via status.FromError.
Solutions
- Check for races between conn.Close() and RPC use: ensure Close() is only called after all RPCs on that ClientConn have finished (e.g., wait on the RPC calls/streams or a WaitGroup before closing).
- If the connection is already closed, do not reuse the ClientConn — create a new one (grpc.NewClient / grpc.DialContext) for further calls.
- Do not string-match or errors.Is against the internal errConnClosing (it is unexported). If you need to detect connection shutdown programmatically, check the status code: errCode, ok := status.FromError(err); use errCode.Code() == codes.Canceled, per the deprecation note on ErrClientConnClosing.
- For the idleness variant, either keep the channel active, disable/extend idleness via grpc.WithIdleTimeout, or handle reconnects by retrying on a fresh connection.
Example fix
// before:
typedConfig := &anypb.Any{
TypeUrl: "", // or "grpc.authz.audit_logging/"
Value: rawJSON,
}
// after:
typedConfig := &anypb.Any{
TypeUrl: "grpc.authz.audit_logging/stdout",
Value: rawJSON,
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the TypeURL resolves to a non-empty logger name:
func validateTypeURL(typeURL string) error {
parts := strings.Split(typeURL, "/")
name := parts[len(parts)-1]
if name == "" {
return fmt.Errorf("type_url %q resolves to empty logger name", typeURL)
}
if audit.GetLoggerBuilder(name) == nil {
return fmt.Errorf("no logger builder registered for %q", name)
}
return nil
} Prevention
- Always set type_url to a URL-formatted string with the logger name after the last '/' (e.g., grpc.authz.audit_logging/stdout).
- Cross-reference the type_url name against audit.GetLoggerBuilder before sending the config.
- Use consistent naming conventions for custom audit logger registrations.
When it happens
Trigger: Calling an RPC on a ClientConn while the connection is being closed — most commonly invoking an RPC after (or concurrently with) conn.Close(). Also hit when a SubConn's transport is closing/draining (see the sibling errConnDrain) or being closed for channel idleness (errConnIdling), so a picker hands out an RPC right as the transport goes away. Distinguish it from the deprecated exported ErrClientConnClosing (codes.Canceled, 'grpc: the client connection is closing') which is returned when the ClientConn itself is closing.
Common situations: Application code that closes the ClientConn (defer conn.Close()) while outstanding or in-flight RPCs are still running; shared-connection code where one goroutine tears down the connection while another dials or invokes RPCs; test harnesses that Close() the conn in t.Cleanup before a background RPC finishes; server-side connection teardown racing a client's streaming call. If you see the variant 'grpc: the connection is closing due to channel idleness', the channel entered idle mode (see grpc.WithIdleTimeout).
Related errors
- grpc: credentials.Bundle must return non-nil transport…
- grpc: no transport security set (use…
- grpc: the connection is drained
- error parsing custom audit logger config
- failed to parse AuditCondition
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/97225e157f834de3.
Report an issue: GitHub.
Appendix: 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 0c51461d27)