grpc/grpc-go · error

xds: json.Unmarshal(%s) failed during bootstrap: %v

Error message

xds: json.Unmarshal(%s) failed during bootstrap: %v

What it means

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.

Source

Thrown at internal/xds/bootstrap/bootstrap.go:598

		XDSServers:                                c.xDSServers,
		CertificateProviders:                      c.cpcs,
		ServerListenerResourceNameTemplate:        c.serverListenerResourceNameTemplate,
		ClientDefaultListenerResourceNameTemplate: c.clientDefaultListenerResourceNameTemplate,
		Authorities:                               c.authorities,
		Node:                                      c.node,
	}
	return json.MarshalIndent(config, " ", " ")
}

// UnmarshalJSON takes the json data (the complete bootstrap configuration) and
// unmarshals it to the struct.
func (c *Config) UnmarshalJSON(data []byte) error {
	// Initialize the node field with client controlled values. This ensures
	// even if the bootstrap configuration did not contain the node field, we
	// will have a node field with client controlled fields alone.
	config := configJSON{Node: newNode()}
	if err := json.Unmarshal(data, &config); err != nil {
		return fmt.Errorf("xds: json.Unmarshal(%s) failed during bootstrap: %v", string(data), err)
	}

	c.xDSServers = config.XDSServers
	c.cpcs = config.CertificateProviders
	c.serverListenerResourceNameTemplate = config.ServerListenerResourceNameTemplate
	c.clientDefaultListenerResourceNameTemplate = config.ClientDefaultListenerResourceNameTemplate
	c.authorities = config.Authorities
	c.node = config.Node

	// Build the certificate providers configuration to ensure that it is valid.
	cpcCfgs := make(map[string]*certprovider.BuildableConfig)
	getBuilder := internal.GetCertificateProviderBuilder.(func(string) certprovider.Builder)
	for instance, nameAndConfig := range c.cpcs {
		name := nameAndConfig.PluginName
		parser := getBuilder(nameAndConfig.PluginName)
		if parser == nil {
			// We ignore plugins that we do not know about.
			continue

View on GitHub (pinned to 03255a9237)

Solutions

  1. Run the bootstrap content through a JSON linter (e.g. jq . < bootstrap.json) to find the syntax error and fix it.
  2. 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.
  3. If the file is injected by a sidecar or controller, verify the injector template and the mounted file contents rather than editing in-pod.
  4. 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.

Example fix

// before — file contains a trailing comma:
//   "xds_servers": [ {...}, ],
//   "node": { "id": "x" }
//
// after — remove trailing comma:
//   "xds_servers": [ {...} ],
//   "node": { "id": "x" }
Defensive patterns

Strategy: validation

Validate before calling

// Validate bootstrap JSON before passing to NewConfigFromContents.
func validateBootstrapJSON(data []byte) error {
    var probe map[string]json.RawMessage
    if err := json.Unmarshal(data, &probe); err != nil {
        return fmt.Errorf("bootstrap is not valid JSON: %w", err)
    }
    return nil
}
// usage:
//   if err := validateBootstrapJSON(data); err != nil { return err }
//   cfg, err := bootstrap.NewConfigFromContents(data)

Try / catch

// When reading the bootstrap at startup, wrap NewConfigFromContents:
cfg, err := bootstrap.NewConfigFromContents(data)
if err != nil {
    return fmt.Errorf("fatal: bootstrap config invalid, application cannot start: %w", err)
}
// Do not fall back to defaults silently — fail fast.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/cbfbf1d0667fc52e. Report an issue: GitHub.