{"record":{"id":"78555b530c6f9186","repo":"grpc/grpc-go","slug":"xds-fetching-trusted-roots-from-certificateprovid","errorCode":null,"errorMessage":"xds: fetching trusted roots from CertificateProvider failed: %v","messagePattern":"xds: fetching trusted roots from CertificateProvider failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credentials/xds/handshake_info.go","lineNumber":224,"sourceCode":"\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\n\t// If AutoHostSNI is true, and the endpoint hostname is present, we use the\n\t// endpoint hostname as the SNI value and also for SAN validation.\n\t// Otherwise, we use the SNI value from HandshakeInfo (which is configured\n\t// by the control plane) and validating SANs based on that.\n\tsni := hi.sni\n\tif hi.useAutoHostSNI && hostname != \"\" {\n\t\tsni = hostname\n\t}\n\n\tcfg.VerifyPeerCertificate = hi.buildVerifyFunc(km, true, sni)\n\n\tif hi.identityProvider != nil {\n\t\tkm, err := hi.identityProvider.KeyMaterial(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"xds: fetching identity certificates from CertificateProvider failed: %v\", err)","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/credentials/xds/handshake_info.go#L206-L242","documentation":"Raised on the client side during xDS-driven TLS when hi.rootProvider.KeyMaterial(ctx) returns an error. The root certificate provider (typically a file-watcher or xDS-cert-provider plugin) could not supply the trusted root CA material needed to verify the server.","triggerScenarios":"The root cert file referenced by the CertificateProvider is missing, unreadable, or malformed; the xDS control plane did not deliver a CertificateProviderInstance for roots; the provider was closed or its context was cancelled; refresh from the management server failed.","commonSituations":"Secret mount path wrong or empty in the pod; mTLS root bundle not yet rotated in; xDS LDS/CDS resource lacks the security config; provider plugin version mismatch; filesystem permission error reading the CA bundle.","solutions":["Check the CertificateProvider config (file path / xDS resource name) and that the root bundle exists and is readable.","Inspect the wrapped error for the provider-specific cause (file not found, parse error, context cancelled).","Verify the xDS management server is sending a CertificateProviderInstance with root certs for this cluster.","Confirm the provider is not closed before handshake and the context is not already cancelled.","Ensure the CA bundle file is valid PEM and not empty."],"exampleFix":"// before: root cert file path missing -> provider.KeyMaterial errors\n// after: mount /etc/grpc/certs/ca.pem and configure file-watcher provider to that path","handlingStrategy":"try-catch","validationCode":"func rootProviderHealthy(p certprovider.Provider) bool {\n    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n    defer cancel()\n    km, err := p.KeyMaterial(ctx)\n    return err == nil && km != nil && km.Roots != nil && len(km.Roots.Subjects()) > 0\n}","typeGuard":null,"tryCatchPattern":"In ClientSideTLSConfig's caller, distinguish useFallback from err; if err mentions 'fetching trusted roots', surface it as a config/secret problem and fail fast rather than retrying in a hot loop.","preventionTips":["Mount root CA bundles via a provisioned secret and validate presence at startup.","Health-check the certificate provider before serving traffic.","Alert on provider KeyMaterial errors so rotation gaps are caught early."],"tags":["grpc","xds","tls","certificates","security","config"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}