grpc/grpc-go · error
received Cluster resource that contains invalid security…
Error message
received Cluster resource that contains invalid security config: %v
What it means
UpdateClientConnState calls handleSecurityConfig to materialize certificate providers for the Cluster resource's SecurityCfg (clusterimpl.go:430-436). If a referenced provider instance is not declared in the bootstrap configuration's certificate_providers map, buildProvider/buildProviders fails and the error is wrapped here. The whole update is rejected rather than silently falling back to insecure credentials.
Solutions
- Add the missing certificate provider instance to the bootstrap file's certificate_providers map with matching plugin_name.
- Verify RootInstanceName/IdentityInstanceName/RootCertName/IdentityCertName in the xDS Cluster resource match the bootstrap keys exactly.
- Regenerate the bootstrap file using the same tooling/config as the control plane deployment.
Example fix
// before (bootstrap.json)
{
"xds_servers": [...],
"certificate_providers": {}
}
// after
{
"xds_servers": [...],
"certificate_providers": {
"default": {
"plugin_name": "file_watcher",
"config": {"certificate_file": "/etc/cert.pem", "private_key_file": "/etc/key.pem", "ca_file": "/etc/ca.pem", "refresh_interval": "300s"}
}
}
} Defensive patterns
Strategy: validation
Validate before calling
// Before dialing, ensure every cert provider instance referenced by the control plane is in the bootstrap.
func validateSecurityConfig(bootstrapCfg *bootstrap.Config, securityCfg *xdsresource.SecurityConfig) error {
if securityCfg == nil { return nil }
cpc := bootstrapCfg.CertProviderConfigs()
for _, name := range []string{securityCfg.RootInstanceName, securityCfg.IdentityInstanceName} {
if name == "" { continue }
if _, ok := cpc[name]; !ok {
return fmt.Errorf("cert provider instance %q missing from bootstrap", name)
}
}
return nil
} Prevention
- Generate the bootstrap file with the same tooling that manages the control plane security config.
- Add a startup self-test that materializes each referenced provider via buildProvider.
- Treat security config drift as a deployment blocker, not a runtime warning.
When it happens
Trigger: clusterUpdate.SecurityCfg.RootInstanceName or IdentityInstanceName is not present in bootstrap.CertificateProviderConfigs(); buildProviderFunc returns the "xds: failed to get security plugin instance" error because cfg.Build() cannot find the named provider.
Common situations: Bootstrap file generated without the cert provider instance the control plane references; new security config deployed on the management server without updating client bootstrap; mismatched instance names between bootstrap and Cluster resource; certificate provider plugin not registered.
Related errors
- child policy not registered
- failed to build call credentials from bootstrap for
- failed to build credentials bundle from bootstrap for
- missing server_listener_resource_name_template in the…
- unexpected balancer config with type: %T
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/0d1811f32c88b7f7.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/balancer/clusterimpl/clusterimpl.go:434
c := xdsclient.FromResolverState(s.ResolverState)
if c == nil {
return balancer.ErrBadResolverState
}
b.xdsClient = c
}
xdsConfig := xdsresource.XDSConfigFromResolverState(s.ResolverState)
if xdsConfig == nil {
b.logger.Warningf("Received balancer config with no xDS config")
return balancer.ErrBadResolverState
}
clusterCfg := xdsConfig.Clusters[newConfig.Cluster]
clusterUpdate := clusterCfg.Config.Cluster
if err := b.handleSecurityConfig(clusterUpdate.SecurityCfg); err != nil {
// If the security config is invalid, for example, if the provider
// instance is not found in the bootstrap config, we need to put the
// channel in transient failure.
return fmt.Errorf("received Cluster resource that contains invalid security config: %v", err)
}
// Update load reporting config. This needs to be done before updating the
// child policy because we need the loadStore from the updated client to be
// passed to the ccWrapper, so that the next picker from the child policy
// will pick up the new loadStore.
if err := b.updateLoadStore(clusterUpdate); err != nil {
return err
}
// Build config for the gracefulswitch balancer. It is safe to ignore JSON
// marshaling errors here, since the config was already validated as part of
// ParseConfig().
cfg := []map[string]any{{newConfig.ChildPolicy.Name: newConfig.ChildPolicy.Config}}
cfgJSON, _ := json.Marshal(cfg)
parsedCfg, err := gracefulswitch.ParseConfig(cfgJSON)
if err != nil {
return errView on GitHub (pinned to 0c51461d27)