grpc/grpc-java · critical · XdsInitializationException

Invalid bootstrap: 'xds_servers' does not exist.

Error message

Invalid bootstrap: 'xds_servers' does not exist.

What it means

During bootstrap parsing, BootstrapperImpl.bootstrapBuilder() requires a top-level 'xds_servers' array in the bootstrap configuration. If JsonUtil.getList(rawData, "xds_servers") returns null, the configuration is rejected with XdsInitializationException because the client has no control-plane servers to connect to.

Source

Thrown at xds/src/main/java/io/grpc/xds/client/BootstrapperImpl.java:121

      throw new XdsInitializationException("Failed to parse JSON", e);
    }

    logger.log(XdsLogLevel.DEBUG, "Bootstrap configuration:\n{0}", rawBootstrap);
    return bootstrap(rawBootstrap);
  }

  @Override
  public BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
    return bootstrapBuilder(rawData).build();
  }

  protected BootstrapInfo.Builder bootstrapBuilder(Map<String, ?> rawData)
      throws XdsInitializationException {
    BootstrapInfo.Builder builder = BootstrapInfo.builder();

    List<?> rawServerConfigs = JsonUtil.getList(rawData, "xds_servers");
    if (rawServerConfigs == null) {
      throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' does not exist.");
    }
    List<ServerInfo> servers = parseServerInfos(rawServerConfigs, logger);
    if (servers.size() > 1 && !enableXdsFallback) {
      servers = ImmutableList.of(servers.get(0));
    }
    builder.servers(servers);

    Node.Builder nodeBuilder = Node.newBuilder();
    Map<String, ?> rawNode = JsonUtil.getObject(rawData, "node");
    if (rawNode != null) {
      String id = JsonUtil.getString(rawNode, "id");
      if (id != null) {
        logger.log(XdsLogLevel.INFO, "Node id: {0}", id);
        nodeBuilder.setId(id);
      }
      String cluster = JsonUtil.getString(rawNode, "cluster");
      if (cluster != null) {
        logger.log(XdsLogLevel.INFO, "Node cluster: {0}", cluster);

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add a top-level "xds_servers" array to the bootstrap JSON with at least one server object containing "server_uri" and "channel_creds".
  2. Fix the key spelling — it must be exactly "xds_servers".
  3. Validate the file against the gRPC xDS bootstrap v3 JSON schema before deploying.
  4. Regenerate the bootstrap file using your mesh's official bootstrap generation tool.

Example fix

// before
{ "node": { "id": "backend" } }
// after
{
  "xds_servers": [{ "server_uri": "dns:///xds.example.com:443",
                     "channel_creds": [{"type": "insecure"}] }]
}
Defensive patterns

Strategy: validation

Validate before calling

java.util.Map<String, ?> cfg = parseBootstrapJson();
if (!(cfg.get("xds_servers") instanceof java.util.List) ||
    ((java.util.List<?>) cfg.get("xds_servers")).isEmpty()) {
  throw new IllegalStateException("bootstrap missing non-empty 'xds_servers' list");
}

Type guard

boolean hasXdsServers(java.util.Map<String, ?> cfg) {
  return cfg.get("xds_servers") instanceof java.util.List
      && !((java.util.List<?>) cfg.get("xds_servers")).isEmpty();
}

Prevention

When it happens

Trigger: Bootstrap JSON lacking the 'xds_servers' key entirely, misspelling it (e.g. 'xdsServers', 'xds_server'), or providing a non-list value for it; empty JSON object {} as the bootstrap config.

Common situations: Following an outdated or wrong-version bootstrap spec (pre-xDS bootstrap v2/v3 examples); hand-writing minimal bootstrap files; tools that emit ADS config under a different key.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/2be4c0ded3f43290. Report an issue: GitHub.