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

  1. 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).
  2. If the connection is already closed, do not reuse the ClientConn — create a new one (grpc.NewClient / grpc.DialContext) for further calls.
  3. 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.
  4. 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

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


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)