NationalSecurityAgency/ghidra · error · ElasticException

Multi-search for exe records failed

Error message

Multi-search for exe records failed

What it means

Thrown in queryExecutableRecordById when one sub-response in an Elasticsearch _msearch (multi-search) bulk request is missing its "hits" field, as detected by ElasticConnection.isNull(hits). Each sub-response in an msearch should contain a hits object; its absence means that individual search failed (e.g., the error envelope replaced the normal hits object).

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java:1131

		int count = 0;
		for (int i = 0; i < maxDocuments; ++i) {
			String exeId = iter1.next().generateExeIdString();
			buffer.append("{}\n");		// Keep default index and type
			buffer.append("{ \"query\": { \"bool\": { \"filter\": { \"term\": { \"_id\": \"");
			buffer.append(exeId);
			buffer.append("\" }}}}}\n");
			count += 1;
			if (!iter1.hasNext()) {
				break;
			}
		}
		JsonObject bulkobj = connection.executeBulk(path, buffer.toString());
		JsonArray responses = (JsonArray) bulkobj.get("responses");
		for (int i = 0; i < count; ++i) {
			JsonObject subquery = (JsonObject) responses.get(i);
			JsonElement hits = subquery.get("hits");
			if (ElasticConnection.isNull(hits)) {
				throw new ElasticException("Multi-search for exe records failed");
			}
			JsonObject totalRec = (JsonObject) ((JsonObject) hits).get("total");
			long total = totalRec.get("value").getAsLong();
			if (total != 1) {
				throw new ElasticException("Could not recover unique executable via id");
			}
		}
		for (int i = 0; i < count; ++i) {
			JsonObject subquery = (JsonObject) responses.get(i);
			JsonObject hits = (JsonObject) subquery.get("hits");
			JsonArray hitsArray = (JsonArray) hits.get("hits");
			hits = (JsonObject) hitsArray.get(0);
			ExecutableRecord newExe = makeExecutableRecord(manager, hits);
			RowKey rowKey = iter2.next();
			manager.cacheExecutableByRow(newExe, rowKey);
		}
	}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Inspect the individual sub-response object (subquery) that triggered the failure to read the embedded error type and reason for the specific root cause.
  2. Ensure no concurrent deletion operations are running against the repository while queries execute.
  3. Check Elasticsearch cluster health and shard allocation; wait for green before retrying.
  4. Retry the query after the transient shard issue resolves.
Defensive patterns

Strategy: retry

Try / catch

int retries = 0;
while (true) {
    try {
        database.query(query);
        break;
    } catch (ElasticException e) {
        if (e.getMessage().contains("Multi-search for exe records failed") && retries < 2) {
            retries++;
            continue; // transient shard issue — retry after brief pause
        }
        throw e;
    }
}

Prevention

When it happens

Trigger: Called when resolving executable records by document id in bulk via repository_executable/_msearch. After executeBulk returns, each subquery response is checked for a hits key. If a shard is unavailable, the index was deleted, or the document type mapping changed, the sub-response carries an error object instead of hits.

Common situations: An executable document was deleted between id collection and the msearch; a shard is relocating or recovering during the query; the executable index mapping was altered; Elasticsearch returns a 4xx-level error for a specific sub-request.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/c1a125faa73463b9. Report an issue: GitHub.