{"record":{"id":"61e76f574a7fc79c","repo":"openzipkin/zipkin","slug":"no-valid-endpoints-found-in-es-hosts-hosts","errorCode":null,"errorMessage":"No valid endpoints found in ES hosts: {hosts}","messagePattern":"No valid endpoints found in ES hosts: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"critical","filePath":"zipkin-server/src/main/java/zipkin2/server/internal/elasticsearch/InitialEndpointSupplier.java","lineNumber":70,"sourceCode":"      int port = getPort(url);\n\n      if (port == 9300) {\n        LOGGER.warn(\"Native transport no longer supported. Changing {} to http port 9200\", host);\n        port = 9200;\n      }\n\n      if (isIpAddress(host) || host.equals(\"localhost\")) {\n        endpointGroups.add(EndpointGroup.of(Endpoint.of(host, port)));\n      } else {\n        // A host that isn't an IP may resolve to multiple IP addresses, so we use a endpoint\n        // group to round-robin over them. Users can mix addresses that resolve to multiple IPs\n        // with single IPs freely, they'll all get used.\n        endpointGroups.add(DnsAddressEndpointGroup.builder(host).port(port).build());\n      }\n    }\n\n    if (endpointGroups.isEmpty()) {\n      throw new IllegalArgumentException(\"No valid endpoints found in ES hosts: \" + hosts);\n    }\n\n    return EndpointGroup.of(endpointGroups);\n  }\n\n  int getPort(URI url) {\n    int port = url.getPort();\n    return port != -1 ? port : sessionProtocol.defaultPort();\n  }\n\n  static boolean isIpAddress(String address) {\n    return zipkin2.Endpoint.newBuilder().parseIp(address);\n  }\n\n  @Override public String toString() {\n    return hosts;\n  }\n}","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin-server/src/main/java/zipkin2/server/internal/elasticsearch/InitialEndpointSupplier.java#L52-L88","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set hosts to at least one valid endpoint, e.g. zipkin.storage.elasticsearch.hosts=http://elasticsearch:9200","Check each comma-separated entry has scheme://host:port form and no stray whitespace","If using env vars, confirm ES hosts variable is set in the actual runtime environment"],"exampleFix":"# before\nzipkin.storage.elasticsearch.hosts=\n\n# after\nzipkin.storage.elasticsearch.hosts=http://elasticsearch:9200","handlingStrategy":"validation","validationCode":"String hosts = requiredEnv(\"ES_HOSTS\");\nif (hosts == null || hosts.isBlank() || hosts.chars().allMatch(c -> c == ',')) {\n  throw new IllegalStateException(\"ES_HOSTS must contain at least one scheme://host:port entry\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail startup config checks when hosts is empty or only delimiters","Smoke-test endpoint URLs with a URI parse before handing them to Zipkin"],"tags":["zipkin","elasticsearch","endpoints","configuration","startup"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}