{"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/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/bootstrap/bootstrap.go#L580-L616","documentation":"This error fires inside Config.UnmarshalJSON (bootstrap.go:598) when Go's encoding/json cannot parse the raw bootstrap bytes into the configJSON struct. It means the bootstrap content is not valid JSON or contains a field whose value type does not match the expected struct tag. The original data is echoed in the message so you can see exactly what bytes failed.","triggerScenarios":"Calling NewConfigFromContents or GetConfiguration with a bootstrap whose JSON is syntactically broken (trailing comma, unquoted key, stray characters) or whose field values have the wrong type (e.g. xds_servers as a string instead of an array). Any path through UnmarshalJSON where json.Unmarshal(data, &config) returns non-nil hits this.","commonSituations":"The GRPC_XDS_BOOTSTRAP file was hand-edited and a typo was introduced; a templating/sidecar injector produced truncated JSON; a copy-paste left a YAML fragment in a JSON file; the node field was set to a string instead of an object.","solutions":["Run the bootstrap content through a JSON linter (e.g. jq . < bootstrap.json) to find the syntax error and fix it.","Compare the structure field-by-field against the configJSON struct tags in bootstrap.go (xds_servers, certificate_providers, node, authorities, etc.) and correct any type mismatches.","If the file is injected by a sidecar or controller, verify the injector template and the mounted file contents rather than editing in-pod.","Add a startup self-check that calls NewConfigFromContents on the file before the gRPC channel is created so the failure surfaces early with a clear log line."],"exampleFix":"// before — file contains a trailing comma:\n//   \"xds_servers\": [ {...}, ],\n//   \"node\": { \"id\": \"x\" }\n//\n// after — remove trailing comma:\n//   \"xds_servers\": [ {...} ],\n//   \"node\": { \"id\": \"x\" }","handlingStrategy":"validation","validationCode":"// Validate bootstrap JSON before passing to NewConfigFromContents.\nfunc validateBootstrapJSON(data []byte) error {\n    var probe map[string]json.RawMessage\n    if err := json.Unmarshal(data, &probe); err != nil {\n        return fmt.Errorf(\"bootstrap is not valid JSON: %w\", err)\n    }\n    return nil\n}\n// usage:\n//   if err := validateBootstrapJSON(data); err != nil { return err }\n//   cfg, err := bootstrap.NewConfigFromContents(data)","typeGuard":null,"tryCatchPattern":"// When reading the bootstrap at startup, wrap NewConfigFromContents:\ncfg, err := bootstrap.NewConfigFromContents(data)\nif err != nil {\n    return fmt.Errorf(\"fatal: bootstrap config invalid, application cannot start: %w\", err)\n}\n// Do not fall back to defaults silently — fail fast.","preventionTips":["Lint the bootstrap JSON in CI with jq or a JSON schema before deploying.","Generate bootstrap files with a templating system that produces valid JSON, not hand-editing.","Add a startup readiness probe that calls NewConfigFromContents so misconfiguration blocks startup visibly."],"tags":["xds","bootstrap","json","config","grpc"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}