{"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":"error","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/0c51461d27177d997e14c642fe18c11668fc09a3/internal/credentials/xds/handshake_info.go#L191-L227","documentation":"Returned by clientSideTLSConfigInternal when hi.rootProvider is nil. On the client side, the root certificate provider is mandatory—it supplies the trusted CA certificates needed to validate the server's certificate chain. Without it, the TLS handshake cannot verify the peer, so gRPC refuses to proceed. The error message directs you to the management server because root provider configuration originates from the xDS DownstreamTLSContext's validation context.","triggerScenarios":"Client-side xDS TLS handshake where the HandshakeInfo exists and is not fallback (UseFallbackCreds() is false) but rootProvider was never set—i.e., NewHandshakeInfo was called with a nil rootProvider. This happens when the xDS security config has an identity provider but no validation/trusted-roots context.","commonSituations":"xDS DownstreamTLSContext is configured with a certificate provider for client identity (mTLS) but omits the validation context (trusted CA); xDS management server sends a partially-populated security policy; misconfigured Istio/Envoy PeerAuthentication or DestinationRule that sets client cert but not CA.","solutions":["On the xDS management server, add a validation context (CertificateValidationContext with trusted_ca) to the DownstreamTLSContext for the client cluster.","Verify the xDS LDS resource includes both CommonTlsContext.certificates (identity) and CommonTlsContext.validation_context (trusted roots).","Use grpc.WithTransportCredentials with fallback credentials if xDS TLS is not strictly required, so the connection falls back when xDS config is incomplete.","Check xDS config dump (e.g., istioctl proxy-config cluster) to confirm the security policy has a validation context."],"exampleFix":"// On the xDS management server (e.g., Istio DestinationRule):\n// before: missing validation context\n// after:\ntls:\n  mode: MUTUAL\n  clientCertificate: /etc/certs/client.pem\n  privateKey: /etc/certs/client.key\n  caCertificates: /etc/certs/ca-cert.pem  # <-- this provides the root provider","handlingStrategy":"validation","validationCode":"// Validate xDS security config has a validation context before relying on xDS TLS.\n// This is config-side validation on the management server.\n// Ensure DownstreamTLSContext has a CertificateValidationContext with trusted_ca.\n// Use istioctl proxy-config to dump and inspect the LDS resource:\n//   istioctl proxy-config listener <pod> -o json | jq '...validation_context...'","typeGuard":null,"tryCatchPattern":"// Check the RPC error and log whether root provider is the issue.\nerr := client.Call(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"fetch trusted roots is missing\") {\n    log.Error(\"xDS security config is missing root cert provider; check management server\")\n}","preventionTips":["Always configure a validation context (trusted CA) alongside identity certs in xDS TLS.","Validate xDS security config completeness in CI/CD before deploying.","Use xDS config linting tools (istioctl analyze) to catch missing fields.","Monitor SDS/cert provider health for root cert delivery."],"tags":["xds","tls","credentials","security-config","certificates","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}