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
- Configure at least one Elasticsearch host (sonar.search.hosts or default localhost:9001) before the client is built.
- Check the configuration source: ensure the property key is spelled correctly and loaded (correct file/environment).
- If hosts come from clustering config, verify the cluster node settings are present.
- 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
- Always configure at least one ES host (default localhost:9001 exists for a reason)
- Assert config lists are non-empty during startup wiring
- Add a startup health check that pings the first host
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
- Can not resolve host [
- Can not resolve host [. Please check network settings and…
- Entries in property must not mix 'host:port' and 'host'…
- Properties [ ] are not allowed when running SonarQube in…
- property need to be set when using elastic search…
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)