{"record":{"id":"dd9e5ad818f5f0e4","repo":"grpc/grpc-go","slug":"xds-certificateprovider-to-fetch-trusted-roots-is","errorCode":null,"errorMessage":"xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server","messagePattern":"xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake\\. Please check configuration on the management server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/credentials/xds/handshake_info.go","lineNumber":209,"sourceCode":"// GetSANMatchersForTesting returns the SAN matchers stored in HandshakeInfo.\n// To be used only for testing purposes.\nfunc (hi *HandshakeInfo) GetSANMatchersForTesting() []matcher.StringMatcher {\n\treturn append([]matcher.StringMatcher{}, hi.sanMatchers...)\n}\n\n// clientSideTLSConfigInternal constructs a tls.Config to be used in a\n// client-side handshake based on the contents of the HandshakeInfo.\n//\n// hostname is passed as a parameter here instead of being part of the\n// HandshakeInfo because HandshakeInfo contains cluster-level security\n// configuration that applies to all endpoints in the cluster, while hostname is\n// specific to each endpoint. This allows sharing a single HandshakeInfo\n// instance across multiple endpoints in the same cluster.\nfunc (hi *HandshakeInfo) clientSideTLSConfigInternal(ctx context.Context, hostname string) (*tls.Config, error) {\n\t// On the client side, rootProvider is mandatory. IdentityProvider is\n\t// optional based on whether the client is doing TLS or mTLS.\n\tif hi.rootProvider == nil {\n\t\treturn nil, errors.New(\"xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server\")\n\t}\n\n\t// InsecureSkipVerify needs to be set to true because we need to perform\n\t// custom verification to check the SAN on the received certificate.\n\t// Currently the Go stdlib does complete verification of the cert (which\n\t// includes hostname verification) or none. We are forced to go with the\n\t// latter and perform the normal cert validation ourselves.\n\tcfg := &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t\tNextProtos:         []string{\"h2\"},\n\t}\n\n\tkm, err := hi.rootProvider.KeyMaterial(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"xds: fetching trusted roots from CertificateProvider failed: %v\", err)\n\t}\n\tcfg.RootCAs = km.Roots\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/credentials/xds/handshake_info.go#L191-L227","documentation":"Returned by HandshakeInfo.clientSideTLSConfigInternal (internal/credentials/xds/handshake_info.go:209) when hi.rootProvider == nil. On the client side the root provider (trusted CA roots used to verify the server) is mandatory — identity provider is optional for plain TLS vs mTLS. A nil root provider means the xDS management server's security config did not supply a validation context / trusted roots for this cluster, so the client cannot safely verify the peer and aborts the TLS handshake.","triggerScenarios":"An xDS-enabled client performs a TLS handshake for a cluster whose DownstreamTlsContext lacks a validation context (no trusted_ca / root cert provider). Surfaced at handshake time via ClientSideTLSConfig -> clientSideTLSConfigInternal, returned from ClientHandshake.","commonSituations":"Management server (Istio/Envoy/Google Traffic Director) misconfiguration: the Listener/Cluster security policy omits the validation context; partial xDS config push where the CDS/LDS resources are inconsistent; using xDS creds on a target whose cluster has no security config and fallback was not used; certificate provider plugin failing to register.","solutions":["Inspect the xDS management server config for the affected cluster and add a validation context with trusted roots (validation_context.trusted_ca / ca_certificate_provider_instance).","Confirm the certprovider plugin referenced by the config is registered in the client binary (e.g. import _ the pemfile/google default plugins).","If no server-side TLS is intended for that cluster, allow fallback credentials instead of forcing xDS TLS.","Check the management server logs / xDS debug dump to see the actual DownstreamTlsContext being sent."],"exampleFix":"// Conceptual: the fix is on the xDS control plane, not in Go code.\n// before (Envoy LDS/CDS): client side lacks validation_context\n// downstream_tls_context:\n//   common_tls_context:\n//     tls_certificates: [{...}]   # identity only, no validation_context\n\n// after\n// downstream_tls_context:\n//   common_tls_context:\n//     tls_certificates: [{...}]\n//     validation_context:\n//       trusted_ca: {filename: \"/etc/grpc/root-ca.pem\"}","handlingStrategy":"validation","validationCode":"// Mostly a control-plane concern; on the client you can detect missing\n// roots early by attempting one TLS handshake and treating the sentinel as\n// a config error. There is no client-side fix — validate operational config:\n// 1. Ensure the certprovider plugin referenced by xDS is imported.\n//    import _ \"google.golang.org/grpc/credentials/tls/certprovider/pemfile\"\n// 2. On the management server, confirm validation_context is set for the cluster.","typeGuard":null,"tryCatchPattern":"// Handshake errors surface from grpc.Dial/Invoke. Retry is not useful until\n// the control plane is fixed; log the sentinel and surface an actionable alert.\nif err != nil && strings.Contains(err.Error(), \"CertificateProvider to fetch trusted roots is missing\") {\n    alertOps(\"xDS control plane missing trusted roots for cluster\")\n}","preventionTips":["Import the certprovider plugins your xDS config references (blank import).","Validate the management server's DownstreamTlsContext includes a validation context in staging.","Set up xDS config monitoring/alerting on security-resource absence."],"tags":["go","grpc","security","xds","tls","handshake","control-plane"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}