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

  1. Add the missing certificate provider instance to the bootstrap file's certificate_providers map with matching plugin_name.
  2. Verify RootInstanceName/IdentityInstanceName/RootCertName/IdentityCertName in the xDS Cluster resource match the bootstrap keys exactly.
  3. 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

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


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 err

View on GitHub (pinned to 0c51461d27)