grpc/grpc-java · critical · XdsInitializationException

Invalid bootstrap: 'xds_servers' is empty

Error message

Invalid bootstrap: 'xds_servers' is empty

What it means

GrpcBootstrapperImpl.bootstrap parses the xDS bootstrap and rejects the result when the servers list is empty, throwing XdsInitializationException "Invalid bootstrap: 'xds_servers' is empty". A bootstrap with no management servers leaves the client unable to establish any xDS stream, so it refuses to initialize.

Source

Thrown at xds/src/main/java/io/grpc/xds/GrpcBootstrapperImpl.java:60

  private static final String BOOTSTRAP_CONFIG_SYS_PROPERTY = "io.grpc.xds.bootstrapConfig";
  @VisibleForTesting
  String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
  @VisibleForTesting
  String bootstrapPathFromSysProp = System.getProperty(BOOTSTRAP_PATH_SYS_PROPERTY);
  @VisibleForTesting
  String bootstrapConfigFromEnvVar = System.getenv(BOOTSTRAP_CONFIG_SYS_ENV_VAR);
  @VisibleForTesting
  String bootstrapConfigFromSysProp = System.getProperty(BOOTSTRAP_CONFIG_SYS_PROPERTY);

  GrpcBootstrapperImpl() {
    super();
  }

  @Override
  public BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
    BootstrapInfo info = super.bootstrap(rawData);
    if (info.servers().isEmpty()) {
      throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' is empty");
    }
    return info;
  }

  /**
   * Gets the bootstrap config as JSON. Searches the config (or file of config) with the
   * following order:
   *
   * <ol>
   *   <li>A filesystem path defined by environment variable "GRPC_XDS_BOOTSTRAP"</li>
   *   <li>A filesystem path defined by Java System Property "io.grpc.xds.bootstrap"</li>
   *   <li>Environment variable value of "GRPC_XDS_BOOTSTRAP_CONFIG"</li>
   *   <li>Java System Property value of "io.grpc.xds.bootstrapConfig"</li>
   * </ol>
   */
  @Override
  protected String getJsonContent() throws XdsInitializationException, IOException {
    String jsonContent;

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add at least one entry to xds_servers in your bootstrap JSON with a valid server_uri and channel_creds.
  2. Check for key typos (xds_server vs xds_servers) or JSON that overrides the array with an empty one.
  3. Print the bootstrap JSON (GrpcBootstrapperImpl bootstrapDump / logs) and confirm the servers list is populated.
  4. If using a file, ensure GRPC_XDS_BOOTSTRAP points to the intended file and the file was fully written.

Example fix

// before
{ "xds_servers": [] }
// after
{ "xds_servers": [ { "server_uri": "xds.example.com:443", "channel_creds": [ { "type": "google_default" } ] } ] }
Defensive patterns

Strategy: validation

Validate before calling

Map<String, ?> boot = /* parsed bootstrap JSON */;
List<?> servers = (List<?>) boot.get("xds_servers");
if (servers == null || servers.isEmpty()) throw new IllegalArgumentException("xds_servers must be non-empty");

Try / catch

try { /* init xDS */ } catch (XdsInitializationException e) { if (e.getMessage().contains("'xds_servers' is empty")) { log.error("Bootstrap has no management servers"); } }

Prevention

When it happens

Trigger: Calling bootstrap(rawData) — or initializing the xDS name resolver/channel — where the resolved bootstrap JSON's xds_servers array is missing or resolves to an empty list.

Common situations: Bootstrap file/env var containing "xds_servers": []; typo like "xds_server"; config merged/overwritten so the array is dropped; control-plane templates rendering no servers.

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/d0535283cacbd143. Report an issue: GitHub.