{"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/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/tls/certprovider/provider.go#L26-L62","documentation":"errProviderClosed (credentials/tls/certprovider/provider.go:44) is returned by Distributor.KeyMaterial once the provider/distributor has been closed. The Provider interface (lines 83-90) exposes Close() to release resources; after Close is invoked, any subsequent KeyMaterial(ctx) call on the same provider instance returns this sentinel. It indicates a use-after-close lifecycle bug in the caller.","triggerScenarios":"Calling provider.Close() (directly or indirectly — e.g. HandshakeInfo.close at handshake_info.go:116-122 closes its root/identity providers) and then calling KeyMaterial on that provider again. Common when a credential provider is shared but closed by one consumer while another still reads it.","commonSituations":"Sharing a certprovider.Provider between channels that are torn down at different times; xDS reconfiguration closing providers that a concurrent handshake still references; manual Close() called too early in a shutdown sequence; double-close of a HandshakeInfo.","solutions":["Stop calling KeyMaterial after Close — audit ownership so exactly one owner closes the provider and no consumer outlives it.","If a provider is shared, use reference counting or clone per consumer instead of closing the shared instance.","Order shutdown so all in-flight handshakes complete (or the context is cancelled) before Close().","Search the codebase for .Close() on provider/certprovider objects and ensure each is the single, final use."],"exampleFix":"// before\np, _ := pemfile.NewProvider(opts)\nkm1, _ := p.KeyMaterial(ctx)\np.Close()\nkm2, _ := p.KeyMaterial(ctx) // err: provider instance is closed\n\n// after\np, _ := pemfile.NewProvider(opts)\nkm, _ := p.KeyMaterial(ctx)\n// ... use km ...\np.Close() // close only after the last consumer is done","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"km, err := provider.KeyMaterial(ctx)\nif err != nil {\n    if errors.Is(err, certprovider.ErrProviderClosed) /* or internal sentinel */ {\n        // provider already closed; stop using it\n        }\n}","preventionTips":["Establish single ownership for Close(): exactly one closer per provider.","For shared providers, reference-count or clone per consumer.","Order shutdown so all consumers drain before Close() is called.","Audit every .Close() call on certprovider.Provider values."],"tags":["go","grpc","security","tls","certprovider","lifecycle","concurrency"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}