grpc/grpc-go · error

xds: required field `xds_servers` not found in bootstrap…

Error message

xds: required field `xds_servers` not found in bootstrap configuration: %s

What it means

Returned by Config.UnmarshalJSON when the xds_servers field is absent or an empty array. xds_servers is the only strictly required top-level field; without at least one management server the xDS client cannot operate.

Solutions

  1. Add an xds_servers array with at least one valid server object.
  2. Verify the GRPC_XDS_BOOTSTRAP / GRPC_XDS_BOOTSTRAP_FILE_NAME env var points to the intended file.
  3. Check the field name spelling (snake_case xds_servers).
  4. Regenerate the bootstrap from your control plane.

Example fix

// before
{"node":{"id":"test"}}

// after
{"xds_servers":[{"server_uri":"trafficdirector.googleapis.com:443","channel_creds":[{"type":"google_default"}]}],"node":{"id":"test"}}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure xds_servers is present and non-empty before deploy.
func hasXDSServers(data []byte) error {
    var top struct {
        XDSServers []json.RawMessage `json:"xds_servers"`
    }
    if err := json.Unmarshal(data, &top); err != nil {
        return err
    }
    if len(top.XDSServers) == 0 {
        return fmt.Errorf("xds_servers missing or empty")
    }
    return nil
}

Try / catch

if _, err := bootstrap.GetConfiguration(); err != nil {
    if strings.Contains(err.Error(), "xds_servers") {
        // add at least one server to the bootstrap.
    }
}

Prevention

When it happens

Trigger: Triggered at bootstrap.go:631 when len(c.xDSServers) == 0 after unmarshalling succeeds structurally.

Common situations: Bootstrap file omits xds_servers; field misspelled (e.g. 'xdsServers'); GRPC_XDS_BOOTSTRAP env var points to a stale or wrong file that lacks the field.

Related errors


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

Appendix: source

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

		parser := getBuilder(nameAndConfig.PluginName)
		if parser == nil {
			// We ignore plugins that we do not know about.
			continue
		}
		bc, err := parser.ParseConfig(nameAndConfig.Config)
		if err != nil {
			return fmt.Errorf("xds: config parsing for certificate provider plugin %q failed during bootstrap: %v", name, err)
		}
		cpcCfgs[instance] = bc
	}
	c.certProviderConfigs = cpcCfgs

	// Default value of the default client listener name template is "%s".
	if c.clientDefaultListenerResourceNameTemplate == "" {
		c.clientDefaultListenerResourceNameTemplate = "%s"
	}
	if len(c.xDSServers) == 0 {
		return fmt.Errorf("xds: required field `xds_servers` not found in bootstrap configuration: %s", string(data))
	}

	// Post-process the authorities' client listener resource template field:
	// - if set, it must start with "xdstp://<authority_name>/"
	// - if not set, it defaults to "xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s"
	for name, authority := range c.authorities {
		prefix := fmt.Sprintf("xdstp://%s", url.PathEscape(name))
		if authority.ClientListenerResourceNameTemplate == "" {
			authority.ClientListenerResourceNameTemplate = prefix + "/envoy.config.listener.v3.Listener/%s"
			continue
		}
		if !strings.HasPrefix(authority.ClientListenerResourceNameTemplate, prefix) {
			return fmt.Errorf("xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q", authority.ClientListenerResourceNameTemplate, name, prefix)
		}
	}
	return nil
}

View on GitHub (pinned to 0c51461d27)