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
- Re-query the parent executable record (queryExecutableRecordById) to refresh ids, then retry the description lookup.
- Verify the Elasticsearch cluster/index the client points at matches the one that produced the cached ids; reconnect with the correct URL.
- If hit during callgraph expansion, ensure the database was ingested with trackcallgraph enabled and documents were committed (refresh) before querying.
- 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
- Do not hold cached function ids across a re-ingest that deletes/recreates documents.
- After bulk re-ingest, force an ES index refresh before querying.
- Point clients at the cluster that owns the ids; never mix clusters.
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
- Could not find function: ${funcName}
- Mismatch in metaid
- meta document does not exist for id=
- Mismatch in vectorid
- vector document does not exist for id=
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/ab4d0e36b703adb9.
Report an issue: GitHub.