{"record":{"id":"dd6061c872a588ee","repo":"grpc/grpc-go","slug":"xds-config-parsing-for-certificate-provider-plugi","errorCode":null,"errorMessage":"xds: config parsing for certificate provider plugin %q failed during bootstrap: %v","messagePattern":"xds: config parsing for certificate provider plugin %q failed during bootstrap: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/bootstrap.go","lineNumber":620,"sourceCode":"\tc.cpcs = config.CertificateProviders\n\tc.serverListenerResourceNameTemplate = config.ServerListenerResourceNameTemplate\n\tc.clientDefaultListenerResourceNameTemplate = config.ClientDefaultListenerResourceNameTemplate\n\tc.authorities = config.Authorities\n\tc.node = config.Node\n\n\t// Build the certificate providers configuration to ensure that it is valid.\n\tcpcCfgs := make(map[string]*certprovider.BuildableConfig)\n\tgetBuilder := internal.GetCertificateProviderBuilder.(func(string) certprovider.Builder)\n\tfor instance, nameAndConfig := range c.cpcs {\n\t\tname := nameAndConfig.PluginName\n\t\tparser := getBuilder(nameAndConfig.PluginName)\n\t\tif parser == nil {\n\t\t\t// We ignore plugins that we do not know about.\n\t\t\tcontinue\n\t\t}\n\t\tbc, err := parser.ParseConfig(nameAndConfig.Config)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"xds: config parsing for certificate provider plugin %q failed during bootstrap: %v\", name, err)\n\t\t}\n\t\tcpcCfgs[instance] = bc\n\t}\n\tc.certProviderConfigs = cpcCfgs\n\n\t// Default value of the default client listener name template is \"%s\".\n\tif c.clientDefaultListenerResourceNameTemplate == \"\" {\n\t\tc.clientDefaultListenerResourceNameTemplate = \"%s\"\n\t}\n\tif len(c.xDSServers) == 0 {\n\t\treturn fmt.Errorf(\"xds: required field `xds_servers` not found in bootstrap configuration: %s\", string(data))\n\t}\n\n\t// Post-process the authorities' client listener resource template field:\n\t// - if set, it must start with \"xdstp://<authority_name>/\"\n\t// - if not set, it defaults to \"xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s\"\n\tfor name, authority := range c.authorities {\n\t\tprefix := fmt.Sprintf(\"xdstp://%s\", url.PathEscape(name))","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/bootstrap/bootstrap.go#L602-L638","documentation":"During bootstrap unmarshalling the library iterates every entry in certificate_providers, looks up the registered provider builder for the plugin name, and calls ParseConfig on that builder's config block. This error fires when a known provider plugin rejects its own config sub-document. The plugin name and the underlying parse error are both reported.","triggerScenarios":"A certificate_providers entry references a registered plugin (e.g. file_watcher) but its inline config object is missing a required field such as certificate_file/private_key_file/ca_certificate_file, or passes a value with the wrong type. Unknown plugin names are silently skipped, so this error only appears for recognized plugins whose ParseConfig returns an error.","commonSituations":"Switching from system-roots to mTLS and forgetting to add certificate_file + private_key_file to the file_watcher config; referencing a cert path that the schema expects as a nested object; mismatch between the file_watcher schema version and the bootstrap format.","solutions":["Check the named plugin in the error message and review its required fields against the pemfile (file_watcher) provider schema: at least one of certificate_file or ca_certificate_file must be set.","Ensure every file path in the config block exists and is readable by the process at bootstrap time.","Validate the certificate_providers stanza with the provider's documentation before deploying.","If the plugin is not actually needed, remove the entry rather than leaving a partial config."],"exampleFix":"// before:\n//   \"certificate_providers\": {\n//     \"default\": { \"plugin_name\": \"file_watcher\", \"config\": {} }\n//   }\n// after:\n//   \"certificate_providers\": {\n//     \"default\": {\n//       \"plugin_name\": \"file_watcher\",\n//       \"config\": { \"certificate_file\": \"/etc/certs/client.pem\",\n//                   \"private_key_file\": \"/etc/certs/client.key\",\n//                   \"ca_certificate_file\": \"/etc/certs/ca.pem\" }\n//     }\n//   }","handlingStrategy":"validation","validationCode":"// Pre-validate the certificate_providers stanza against known schemas.\nfunc validateCertProviders(raw map[string]json.RawMessage) error {\n    for inst, b := range raw {\n        var entry struct{ PluginName string `json:\"plugin_name\"`; Config map[string]any `json:\"config\"` }\n        if err := json.Unmarshal(b, &entry); err != nil {\n            return fmt.Errorf(\"%s: %w\", inst, err)\n        }\n        if entry.PluginName == \"file_watcher\" {\n            if _, ok := entry.Config[\"certificate_file\"]; !ok {\n                if _, ok := entry.Config[\"ca_certificate_file\"]; !ok {\n                    return fmt.Errorf(\"%s: file_watcher needs certificate_file or ca_certificate_file\", inst)\n                }\n            }\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep a JSON schema for the certificate_providers section and validate against it in CI.","Cross-check every plugin config against the pemfile provider documentation before deploy.","Use a bootstrap generator rather than hand-writing the providers map."],"tags":["xds","bootstrap","certificate-provider","file-watcher","config","grpc"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}