{"record":{"id":"cbfbf1d0667fc52e","repo":"grpc/grpc-go","slug":"xds-json-unmarshal-s-failed-during-bootstrap","errorCode":null,"errorMessage":"xds: json.Unmarshal(%s) failed during bootstrap: %v","messagePattern":"xds: json\\.Unmarshal\\((.+?)\\) failed during bootstrap: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/bootstrap.go","lineNumber":598,"sourceCode":"\t\tXDSServers:                                c.xDSServers,\n\t\tCertificateProviders:                      c.cpcs,\n\t\tServerListenerResourceNameTemplate:        c.serverListenerResourceNameTemplate,\n\t\tClientDefaultListenerResourceNameTemplate: c.clientDefaultListenerResourceNameTemplate,\n\t\tAuthorities:                               c.authorities,\n\t\tNode:                                      c.node,\n\t}\n\treturn json.MarshalIndent(config, \" \", \" \")\n}\n\n// UnmarshalJSON takes the json data (the complete bootstrap configuration) and\n// unmarshals it to the struct.\nfunc (c *Config) UnmarshalJSON(data []byte) error {\n\t// Initialize the node field with client controlled values. This ensures\n\t// even if the bootstrap configuration did not contain the node field, we\n\t// will have a node field with client controlled fields alone.\n\tconfig := configJSON{Node: newNode()}\n\tif err := json.Unmarshal(data, &config); err != nil {\n\t\treturn fmt.Errorf(\"xds: json.Unmarshal(%s) failed during bootstrap: %v\", string(data), err)\n\t}\n\n\tc.xDSServers = config.XDSServers\n\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","sourceCodeStart":580,"sourceCodeEnd":616,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/bootstrap/bootstrap.go#L580-L616","documentation":"Returned by Config.UnmarshalJSON when the top-level json.Unmarshal of the bootstrap content into configJSON fails. The whole bootstrap document does not match the expected top-level schema (xds_servers, certificate_providers, authorities, node, etc.).","triggerScenarios":"Triggered at bootstrap.go:598 when json.Unmarshal(data, &config) errors. Usually because a top-level field has the wrong JSON type, e.g. authorities is an array instead of an object, certificate_providers is a string, or node is not an object.","commonSituations":"Hand-edited bootstrap with structural type errors; mixing versions of the bootstrap format; pasting a YAML or a partial object into the bootstrap file.","solutions":["Run the bootstrap through jq to confirm it parses and inspect each top-level field type.","Ensure authorities and certificate_providers are JSON objects (maps), xds_servers is an array, node is an object.","Compare against a known-good bootstrap sample for your control plane.","Regenerate the bootstrap from the control-plane tooling."],"exampleFix":"// before (authorities wrong type)\n\"authorities\": [\"auth1\"]\n\n// after\n\"authorities\": {\"auth1\":{\"client_listener_resource_name_template\":\"xdstp://auth1/envoy.config.listener.v3.Listener/%s\",\"xds_servers\":[...]}}","handlingStrategy":"validation","validationCode":"// Lightweight structural check of top-level bootstrap types.\nfunc validateBootstrapShape(data []byte) error {\n    var top map[string]json.RawMessage\n    if err := json.Unmarshal(data, &top); err != nil {\n        return err\n    }\n    if v, ok := top[\"authorities\"]; ok {\n        var m map[string]json.RawMessage\n        if err := json.Unmarshal(v, &m); err != nil {\n            return fmt.Errorf(\"authorities must be an object: %w\", err)\n        }\n    }\n    if v, ok := top[\"certificate_providers\"]; ok {\n        var m map[string]json.RawMessage\n        if err := json.Unmarshal(v, &m); err != nil {\n            return fmt.Errorf(\"certificate_providers must be an object: %w\", err)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := bootstrap.NewConfigFromContents(data); err != nil {\n    log.Fatalf(\"bootstrap structural error: %v\", err)\n}","preventionTips":["Use a JSON schema validator in CI for bootstrap files.","Keep authorities and certificate_providers as objects, never arrays.","Start from a known-good sample when introducing a new field."],"tags":["grpc","xds","bootstrap","config","json","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}