openzipkin/zipkin · critical · IllegalArgumentException

No valid endpoints found in ES hosts

Error message

No valid endpoints found in ES hosts: {hosts}

What it means

InitialEndpointSupplier throws IllegalArgumentException ('No valid endpoints found in ES hosts: {hosts}') when after parsing the configured ES hosts list, zero usable endpoints were produced. Hosts entries are split and parsed into Armeria endpoints (IP, localhost, or DNS-resolvable names); if every entry fails parsing or the list is empty after filtering, the supplier refuses to build an empty endpoint group.

Solutions

  1. Set hosts to at least one valid endpoint, e.g. zipkin.storage.elasticsearch.hosts=http://elasticsearch:9200
  2. Check each comma-separated entry has scheme://host:port form and no stray whitespace
  3. If using env vars, confirm ES hosts variable is set in the actual runtime environment

Example fix

# before
zipkin.storage.elasticsearch.hosts=

# after
zipkin.storage.elasticsearch.hosts=http://elasticsearch:9200
Defensive patterns

Strategy: validation

Validate before calling

String hosts = requiredEnv("ES_HOSTS");
if (hosts == null || hosts.isBlank() || hosts.chars().allMatch(c -> c == ',')) {
  throw new IllegalStateException("ES_HOSTS must contain at least one scheme://host:port entry");
}

Prevention

When it happens

Trigger: Setting zipkin.storage.elasticsearch.hosts to an empty string, or to entries whose scheme/host/port cannot be parsed (e.g. 'http://', '://es:9200', entries with whitespace or invalid characters), so endpointGroups ends up empty at the check.

Common situations: Empty ES_HOSTS env var in a container, malformed URLs from templating, a hosts string of only delimiters, or a config typo like 'http//es:9200' silently skipping all entries.

Related errors


AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14). Data as JSON: /api/errors/61e76f574a7fc79c. Report an issue: GitHub.

Appendix: source

Thrown at zipkin-server/src/main/java/zipkin2/server/internal/elasticsearch/InitialEndpointSupplier.java:70

      int port = getPort(url);

      if (port == 9300) {
        LOGGER.warn("Native transport no longer supported. Changing {} to http port 9200", host);
        port = 9200;
      }

      if (isIpAddress(host) || host.equals("localhost")) {
        endpointGroups.add(EndpointGroup.of(Endpoint.of(host, port)));
      } else {
        // A host that isn't an IP may resolve to multiple IP addresses, so we use a endpoint
        // group to round-robin over them. Users can mix addresses that resolve to multiple IPs
        // with single IPs freely, they'll all get used.
        endpointGroups.add(DnsAddressEndpointGroup.builder(host).port(port).build());
      }
    }

    if (endpointGroups.isEmpty()) {
      throw new IllegalArgumentException("No valid endpoints found in ES hosts: " + hosts);
    }

    return EndpointGroup.of(endpointGroups);
  }

  int getPort(URI url) {
    int port = url.getPort();
    return port != -1 ? port : sessionProtocol.defaultPort();
  }

  static boolean isIpAddress(String address) {
    return zipkin2.Endpoint.newBuilder().parseIp(address);
  }

  @Override public String toString() {
    return hosts;
  }
}

View on GitHub (pinned to 878ce2a1fa)