elastic/elasticsearch · error · IOException
expected data to start with an object
Error message
expected data to start with an object
What it means
Thrown by readHosts when parsing the /_nodes/http response: the first JSON token must be START_OBJECT (the outer nodes-info envelope). If the parser sees an array, a string, or anything else, the response is not a valid nodes-info document and sniffer cannot extract hosts.
Source
Thrown at client/sniffer/src/main/java/org/elasticsearch/client/sniff/ElasticsearchNodesSniffer.java:114
this.request = new Request("GET", "/_nodes/http");
request.addParameter("timeout", sniffRequestTimeoutMillis + "ms");
this.scheme = Objects.requireNonNull(scheme, "scheme cannot be null");
}
/**
* Calls the elasticsearch nodes info api, parses the response and returns all the found http hosts
*/
@Override
public List<Node> sniff() throws IOException {
Response response = restClient.performRequest(request);
return readHosts(response.getEntity(), scheme, jsonFactory);
}
static List<Node> readHosts(HttpEntity entity, Scheme scheme, JsonFactory jsonFactory) throws IOException {
try (InputStream inputStream = entity.getContent()) {
JsonParser parser = jsonFactory.createParser(inputStream);
if (parser.nextToken() != JsonToken.START_OBJECT) {
throw new IOException("expected data to start with an object");
}
List<Node> nodes = new ArrayList<>();
while (parser.nextToken() != JsonToken.END_OBJECT) {
if (parser.getCurrentToken() == JsonToken.START_OBJECT) {
if ("nodes".equals(parser.getCurrentName())) {
while (parser.nextToken() != JsonToken.END_OBJECT) {
JsonToken token = parser.nextToken();
assert token == JsonToken.START_OBJECT;
String nodeId = parser.getCurrentName();
Node node = readNode(nodeId, parser, scheme);
if (node != null) {
nodes.add(node);
}
}
} else {
parser.skipChildren();
}
}View on GitHub (pinned to db6a809a66)
Solutions
- Verify the sniff endpoint (/_nodes/http) returns the standard ES nodes-info object: curl it directly.
- Ensure the RestClient is pointed at the real ES HTTP layer, not a proxy/HTML gateway.
- If behind a proxy, configure the proxy to pass through to ES unchanged, or sniff a fixed host list instead.
Example fix
// before
RestClient client = RestClient.builder(new HttpHost("proxy", 80, "http")).build();
Sniffer sniffer = Sniffer.builder(client).build();
// after
RestClient client = RestClient.builder(new HttpHost("es-internal", 9200, "http")).build();
Sniffer sniffer = Sniffer.builder(client).build(); Defensive patterns
Strategy: try-catch
Try / catch
try { sniffer.sniff(); }
catch (IOException e) {
if (e.getMessage().equals("expected data to start with an object")) {
// endpoint returned a non-ES body: disable sniffing or fix the proxy
} else throw e;
} Prevention
- curl /_nodes/http directly to confirm the response shape before enabling sniffing.
- Make sure the client talks to ES, not an HTML/JSON-error gateway.
When it happens
Trigger: The endpoint returned a non-JSON-object body: an array of errors, a plain string, HTML error page, or a proxy interstitial. Reached via Sniffer.sniff() -> ElasticsearchNodesSniffer.sniff().
Common situations: Sniffing against a URL that is fronted by a load balancer/proxy returning a different shape; wrong scheme/port hitting a non-ES service; ES returned an error array; authentication challenge returning an HTML page.
Related errors
- Failed to parse content of branches.json
- repeated attribute key [{}]
- ordered {} are not supported
- Gradle is running in offline mode, but branches.json locatio
- sniffRequestTimeoutMillis must be greater than 0
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/2ba8a2d83a3fedd0.
Report an issue: GitHub.