{"record":{"id":"70f8ee4cac2bfbd5","repo":"NationalSecurityAgency/ghidra","slug":"could-not-recover-unique-executable-via-id","errorCode":null,"errorMessage":"Could not recover unique executable via id","messagePattern":"Could not recover unique executable via id","errorType":"exception","errorClass":"ElasticException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java","lineNumber":1136,"sourceCode":"\t\t\tbuffer.append(exeId);\n\t\t\tbuffer.append(\"\\\" }}}}}\\n\");\n\t\t\tcount += 1;\n\t\t\tif (!iter1.hasNext()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tJsonObject bulkobj = connection.executeBulk(path, buffer.toString());\n\t\tJsonArray responses = (JsonArray) bulkobj.get(\"responses\");\n\t\tfor (int i = 0; i < count; ++i) {\n\t\t\tJsonObject subquery = (JsonObject) responses.get(i);\n\t\t\tJsonElement hits = subquery.get(\"hits\");\n\t\t\tif (ElasticConnection.isNull(hits)) {\n\t\t\t\tthrow new ElasticException(\"Multi-search for exe records failed\");\n\t\t\t}\n\t\t\tJsonObject totalRec = (JsonObject) ((JsonObject) hits).get(\"total\");\n\t\t\tlong total = totalRec.get(\"value\").getAsLong();\n\t\t\tif (total != 1) {\n\t\t\t\tthrow new ElasticException(\"Could not recover unique executable via id\");\n\t\t\t}\n\t\t}\n\t\tfor (int i = 0; i < count; ++i) {\n\t\t\tJsonObject subquery = (JsonObject) responses.get(i);\n\t\t\tJsonObject hits = (JsonObject) subquery.get(\"hits\");\n\t\t\tJsonArray hitsArray = (JsonArray) hits.get(\"hits\");\n\t\t\thits = (JsonObject) hitsArray.get(0);\n\t\t\tExecutableRecord newExe = makeExecutableRecord(manager, hits);\n\t\t\tRowKey rowKey = iter2.next();\n\t\t\tmanager.cacheExecutableByRow(newExe, rowKey);\n\t\t}\n\t}\n\n\t/**\n\t * Query for function documents based on their parent executable id.\n\t * A \"page\" of results is selected by selecting a -start- document and a maximum number to return\n\t * @param exeId is the executable id\n\t * @param maxDocuments is the maximum number of functions to return","sourceCodeStart":1118,"sourceCodeEnd":1154,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java#L1118-L1154","documentation":"Thrown in queryExecutableRecordById when the total hit count for a specific executable _id within an msearch sub-response is not exactly 1. Since _id is the Elasticsearch primary key, exactly one document must match; a count of 0 means the document was deleted, and a count greater than 1 indicates index-level corruption or replication inconsistency.","triggerScenarios":"After the hits null-check passes for a sub-response, totalRec.get(\"value\").getAsLong() returns a value other than 1. This happens when the executable document no longer exists (deleted between id collection and the msearch) or when replica shards return inconsistent results.","commonSituations":"Concurrent deletion of the executable record by another process; Elasticsearch replica inconsistency causing stale reads; race condition during concurrent ingest/delete; read from a recovering shard that has not yet indexed the document.","solutions":["Avoid concurrent delete and query operations on the same repository; serialize these operations.","Set number_of_replicas appropriately and wait for green cluster health before querying to minimize stale replica reads.","Re-run the query after concurrent operations have settled.","If persistent, investigate Elasticsearch replication and read-consistency settings (e.g., set ?preference=_primary)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before querying executable records, verify the document still exists\nJsonObject check = connection.executeStatement(ElasticConnection.GET,\n    \"executable/_doc/\" + exeId, \"{}\");\nif (check.get(\"found\") != null && !check.get(\"found\").getAsBoolean()) {\n    // Skip this executable — it was deleted\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    database.query(query);\n} catch (ElasticException e) {\n    if (e.getMessage().contains(\"Could not recover unique executable via id\")) {\n        // Concurrent deletion likely; skip or retry after operations settle\n        Msg.warn(this, \"Executable record not found (possibly deleted concurrently): \" + e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Serialize delete and query operations on the same repository to avoid race conditions.","Set appropriate number_of_replicas and wait for green health before querying.","Use preference=_primary in Elasticsearch queries to reduce stale replica reads."],"tags":["elasticsearch","bsim","concurrency","query","data-integrity","replication"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}