SonarSource/sonarqube · error · IllegalArgumentException

At least one Elasticsearch host is required

Error message

At least one Elasticsearch host is required

What it means

SynchronousEsHttpClient's constructor requires a non-empty list of Elasticsearch HttpHosts; an empty list means there is nowhere to send requests, so it throws IllegalArgumentException immediately. This is a guard against a misconfigured ES client with zero endpoints.

Solutions

  1. Configure at least one Elasticsearch host (sonar.search.hosts or default localhost:9001) before the client is built.
  2. Check the configuration source: ensure the property key is spelled correctly and loaded (correct file/environment).
  3. If hosts come from clustering config, verify the cluster node settings are present.
  4. If constructing programmatically, assert the list is non-empty before calling the constructor.

Example fix

// before
new SynchronousEsHttpClient(httpClient, Collections.emptyList());
// after
new SynchronousEsHttpClient(httpClient, List.of(new HttpHost("http", address, 9001)));
Defensive patterns

Strategy: validation

Validate before calling

if (hosts == null || hosts.isEmpty()) { throw new IllegalStateException("Configure sonar.search.hosts before creating SynchronousEsHttpClient"); }

Try / catch

try { client = new SynchronousEsHttpClient(httpClient, hosts); } catch (IllegalArgumentException e) { if (e.getMessage().contains("At least one Elasticsearch host")) { log.error("No ES hosts configured"); } throw e; }

Prevention

When it happens

Trigger: Constructing SynchronousEsHttpClient with an empty hosts list, which occurs when no sonar.search.hosts entries are configured (properties missing or all filtered out).

Common situations: sonar.search.hosts not set in sonar.properties; configuration binding stripping empty values; cluster bootstrap ordering where ES config is absent.

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 SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/2ec2f77595e3316d. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-server-common/src/main/java/org/sonar/server/es/SynchronousEsHttpClient.java:77

 * call submits the request to an I/O reactor thread and blocks the caller on a future. That extra thread
 * hand-off is nearly free on an idle host but costs milliseconds of scheduling latency per request on a
 * CPU-contended host, which - multiplied over the many small bulk/refresh/search round-trips of an
 * analysis - noticeably slows indexing (see SONAR-30530). This client instead performs the request inline
 * on the calling thread, with no hand-off.
 * <p>
 * Requests are round-robined across the configured hosts. On a connection-establishment failure the next
 * host is tried; other failures (including responses received from a node) are propagated without retry, so
 * a non-idempotent request such as a bulk is never re-sent after it reached a node.
 */
public final class SynchronousEsHttpClient implements TransportHttpClient {

  private final CloseableHttpClient httpClient;
  private final List<HttpHost> hosts;
  private final AtomicInteger nextHost = new AtomicInteger(0);

  public SynchronousEsHttpClient(CloseableHttpClient httpClient, List<HttpHost> hosts) {
    if (hosts.isEmpty()) {
      throw new IllegalArgumentException("At least one Elasticsearch host is required");
    }
    this.httpClient = httpClient;
    this.hosts = List.copyOf(hosts);
  }

  /**
   * The configured Elasticsearch hosts, in the order they were provided.
   */
  public List<HttpHost> hosts() {
    return hosts;
  }

  @Override
  public Response performRequest(String endpointId, @Nullable Node node, Request request, TransportOptions options) throws IOException {
    Map<String, String> headers = request.headers();
    ContentType contentType = null;
    String ct = headers.get(HeaderMap.CONTENT_TYPE);
    if (ct != null) {

View on GitHub (pinned to 184c821202)