NationalSecurityAgency/ghidra · error · ElasticException

No function documents matching id=${rowId}

Error message

No function documents matching id=${rowId}

What it means

Thrown by ElasticDatabase.querySingleDescriptionId when an Elasticsearch _search by document id against the 'executable' index returns zero hits. The method expects exactly one function document for the given rowId (a BSim function document _id); a missing document means the BSim index is out of sync with the in-memory DescriptionManager or the id was stale/invalid. It is an ElasticException, indicating a server-side data-consistency problem rather than a caller bug.

Source

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

	 * @param manager is the container for the new FunctionDescription
	 * @param rowId is the document id of the function
	 * @return the new FunctionDescription
	 * @throws ElasticException for communication problems with the server
	 * @throws LSHException for problems adding records to the container
	 */
	private FunctionDescription querySingleDescriptionId(DescriptionManager manager, String rowId)
			throws ElasticException, LSHException {
		StringBuilder buffer = new StringBuilder();
		buffer.append("{ \"query\": { \"ids\": { \"values\": [ \"");
		buffer.append(rowId);
		buffer.append("\" ] } } }");
		JsonObject resp = connection.executeStatement(ElasticConnection.GET, "executable/_search",
			buffer.toString());
		JsonObject hits = (JsonObject) resp.get("hits");
		JsonObject totalRec = (JsonObject) hits.get("total");
		long total = totalRec.get("value").getAsLong();
		if (total == 0) {
			throw new ElasticException("No function documents matching id=" + rowId);
		}
		JsonArray hitsArray = (JsonArray) hits.get("hits");
		JsonObject row = (JsonObject) hitsArray.get(0);
		JsonObject source = (JsonObject) row.get("_source");
		JsonObject joinfield = (JsonObject) source.get("join_field");
		String exeId = joinfield.get("parent").getAsString();
		RowKeyElastic eKey = RowKeyElastic.parseExeIdString(exeId);
		ExecutableRecord exeRec = manager.findExecutableByRow(eKey);
		if (exeRec == null) {
			List<RowKeyElastic> keyList = new ArrayList<>();
			keyList.add(eKey);
			queryExecutableRecordById(manager, keyList.iterator(), keyList.iterator(), 2);
			exeRec = manager.findExecutableByRow(eKey);
		}
		return convertDescriptionRow(row, exeRec, manager, null);
	}

	/**

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Re-query the parent executable record (queryExecutableRecordById) to refresh ids, then retry the description lookup.
  2. Verify the Elasticsearch cluster/index the client points at matches the one that produced the cached ids; reconnect with the correct URL.
  3. If hit during callgraph expansion, ensure the database was ingested with trackcallgraph enabled and documents were committed (refresh) before querying.
  4. Treat as non-fatal where possible: log the rowId and skip that function rather than aborting the whole query.

Example fix

// before
JsonObject resp = connection.executeStatement(ElasticConnection.GET, "executable/_search", buffer.toString());
// ... if total == 0 throw
// after: re-fetch parent and retry once, else skip
if (total == 0 && !manager.findExecutableByRow(parentKey).wasRefreshed()) {
    manager.invalidate(parentKey);
    return querySingleDescriptionId(manager, rowId);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before resolving a cached function id
// confirm the parent executable still lists this function id
if (!cachedExecutableRecord.hasFunctionId(rowId)) {
    // refresh or skip
    return null;
}

Try / catch

try {
    return querySingleDescriptionId(manager, rowId);
} catch (ElasticException e) {
    if (e.getMessage().startsWith("No function documents matching id=")) {
        log.warning("Stale function id " + rowId + "; skipping");
        return null; // skip rather than abort the whole query
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling fillinChildren / a callgraph or description query that resolves a cached RowKey to a function id that no longer exists in the Elasticsearch index. Happens when the DB was modified (re-indexed, documents deleted) while a query session still holds old ids, or when an id string is malformed so it matches no document.

Common situations: Concurrent re-ingest of an executable that deleted and re-created function documents; pointing a client at the wrong ES cluster whose schema/data differs; a race where the parent executable record exists but its child function documents were not yet indexed.

Related errors


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