{"record":{"id":"ef77f3306c83fbd8","repo":"grpc/grpc-go","slug":"provider-instance-is-closed","errorCode":null,"errorMessage":"provider instance is closed","messagePattern":"provider instance is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/tls/certprovider/provider.go","lineNumber":44,"sourceCode":"\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\n\t\"github.com/spiffe/go-spiffe/v2/bundle/spiffebundle\"\n\t\"google.golang.org/grpc/internal\"\n)\n\nfunc init() {\n\tinternal.GetCertificateProviderBuilder = getBuilder\n}\n\nvar (\n\t// errProviderClosed is returned by Distributor.KeyMaterial when it is\n\t// closed.\n\terrProviderClosed = errors.New(\"provider instance is closed\")\n\n\t// m is a map from name to Provider builder.\n\tm = make(map[string]Builder)\n)\n\n// Register registers the Provider builder, whose name as returned by its Name()\n// method will be used as the name registered with this builder. Registered\n// Builders are used by the Store to create Providers.\nfunc Register(b Builder) {\n\tm[b.Name()] = b\n}\n\n// getBuilder returns the Provider builder registered with the given name.\n// If no builder is registered with the provided name, nil will be returned.\nfunc getBuilder(name string) Builder {\n\tif b, ok := m[name]; ok {\n\t\treturn b\n\t}","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/tls/certprovider/provider.go#L26-L62","documentation":"errProviderClosed is a sentinel error returned by certprovider.Distributor.KeyMaterial and by the closedProvider wrapper when the provider or distributor has been closed (Stop/Close called) and can no longer serve key material. It indicates the certificate provider's lifecycle has ended and subsequent calls for TLS key material will fail.","triggerScenarios":"Calling KeyMaterial on a Provider (or its underlying Distributor) after Close() has been called. Also returned by closedProvider, which the store wraps providers in after their reference count drops to zero. This can happen if a channel is closed and garbage-collected while a goroutine still tries to read key material, or if the provider is explicitly closed too early.","commonSituations":"Race conditions during shutdown where a connection handshake is in progress when the provider is closed. Reference-counting bugs in the certprovider store where a provider is released while still referenced. Long-running goroutines that outlive the channel that owned the provider.","solutions":["Ensure no goroutines are calling KeyMaterial after Close; coordinate shutdown so in-flight handshakes complete first.","If using the certprovider store, rely on its reference counting rather than manually closing providers.","Handle errProviderClosed gracefully in credential wrappers (e.g., by falling back or returning a clear error to the caller)."],"exampleFix":"// before\nkm, err := provider.KeyMaterial(ctx)\nif err != nil { log.Fatal(err) } // crashes on shutdown\n// after\nkm, err := provider.KeyMaterial(ctx)\nif errors.Is(err, certprovider.ErrProviderClosed) { // not exported; check message or stop first\n    return status.Error(codes.Unavailable, \"credential provider shutting down\")\n}","handlingStrategy":"try-catch","validationCode":"// Track provider lifecycle: do not call KeyMaterial after Close.\n// Use a done channel or context to coordinate shutdown.","typeGuard":null,"tryCatchPattern":"km, err := provider.KeyMaterial(ctx)\nif err != nil {\n    if err.Error() == \"provider instance is closed\" {\n        return status.Error(codes.Unavailable, \"credential provider closed\")\n    }\n    return err\n}","preventionTips":["Use the certprovider store's reference counting rather than closing providers manually.","Coordinate shutdown so in-flight handshakes finish before Close.","Avoid long-lived goroutines that outlive the owning channel."],"tags":["go","grpc","certprovider","lifecycle","shutdown"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}