grpc/grpc-go · error

xds: json.Unmarshal( ) failed during bootstrap

Error message

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

What it means

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.).

Solutions

  1. Run the bootstrap through jq to confirm it parses and inspect each top-level field type.
  2. Ensure authorities and certificate_providers are JSON objects (maps), xds_servers is an array, node is an object.
  3. Compare against a known-good bootstrap sample for your control plane.
  4. Regenerate the bootstrap from the control-plane tooling.

Example fix

// before (authorities wrong type)
"authorities": ["auth1"]

// after
"authorities": {"auth1":{"client_listener_resource_name_template":"xdstp://auth1/envoy.config.listener.v3.Listener/%s","xds_servers":[...]}}
Defensive patterns

Strategy: validation

Validate before calling

// Lightweight structural check of top-level bootstrap types.
func validateBootstrapShape(data []byte) error {
    var top map[string]json.RawMessage
    if err := json.Unmarshal(data, &top); err != nil {
        return err
    }
    if v, ok := top["authorities"]; ok {
        var m map[string]json.RawMessage
        if err := json.Unmarshal(v, &m); err != nil {
            return fmt.Errorf("authorities must be an object: %w", err)
        }
    }
    if v, ok := top["certificate_providers"]; ok {
        var m map[string]json.RawMessage
        if err := json.Unmarshal(v, &m); err != nil {
            return fmt.Errorf("certificate_providers must be an object: %w", err)
        }
    }
    return nil
}

Try / catch

if _, err := bootstrap.NewConfigFromContents(data); err != nil {
    log.Fatalf("bootstrap structural error: %v", err)
}

Prevention

When it happens

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

Common situations: Hand-edited bootstrap with structural type errors; mixing versions of the bootstrap format; pasting a YAML or a partial object into the bootstrap file.

Related errors


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

Appendix: 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 0c51461d27)