NationalSecurityAgency/ghidra · error · ElasticException

reason

Error message

reason

What it means

In insertExecutableRecord the executable document is PUT with op_type=create; a version_conflict error type is tolerated (means 'already exists' -> return false). Any OTHER error in the ES response causes an ElasticException carrying the raw 'reason' string from the Elasticsearch error JSON. So the message text is whatever Elasticsearch reported.

Source

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

		if (catrecs != null) {
			builder.append(", ");
			appendCategoryTag(catrecs, builder);
		}
		builder.append(", \"join_field\": \"exe\" }");
		StringBuilder pathbuilder = new StringBuilder();
		pathbuilder.append("executable/_doc/");
		pathbuilder.append(exeId);
		pathbuilder.append("?op_type=create");		// Do "create" operation, so we fail if document already exists
		JsonObject resp = connection.executeStatementExpectFailure(ElasticConnection.PUT,
			pathbuilder.toString(), builder.toString());
		JsonObject error = (JsonObject) resp.get("error");
		if (error != null) {
			String type = error.get("type").getAsString();
			if (type.startsWith("version_conflict")) {
				return false;			// Document already inserted
			}
			String reason = ElasticConnection.convertToString(error.get("reason"));
			throw new ElasticException(reason);
		}
		return true;
	}

	/**
	 * Set the "document id" for an ExecutableRecord. This is currently the
	 * last 96-bits of the md5 hash of the executable encoded in base64
	 * @param manager is the container for the ExecutableRecord
	 * @param exeRecord has its key set
	 * @return the new RowKey
	 */
	private static RowKeyElastic updateKey(DescriptionManager manager, ExecutableRecord exeRecord) {
		if (exeRecord.getRowId() == null) {
			RowKeyElastic eKey = new RowKeyElastic(exeRecord.getMd5());
			manager.setExeRowId(exeRecord, eKey);
			return eKey;
		}
		return (RowKeyElastic) exeRecord.getRowId();

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Read the 'reason' string -- it is the verbatim Elasticsearch message -- and address that root cause.
  2. Check cluster health and readonly blocks: GET _cluster/settings; clear index.blocks.read_only_allow_write if set (flood-stage disk watermark).
  3. Verify the executable document JSON and index mapping are well-formed and consistent.
  4. Confirm ES server version compatibility with the BSim client.

Example fix

// before
// unhandled: the raw ES reason bubbles up opaquely
// after
try {
    db.insert(req);
} catch (ElasticException e) {
    if (e.getMessage().contains("read_only_allow_indices")) {
        clearReadOnlyBlock(db.getDatabaseName());
        // retry once
    } else {
        throw e;
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap pre-flight: cluster must be writable.
if (cluster.hasReadOnlyBlock(dbName)) {
    throw new IllegalStateException("Cluster is read-only; cannot ingest");
}

Try / catch

// Inspect the ES reason; retry only on transient/readonly causes.
try {
    db.insert(req);
} catch (ElasticException e) {
    String r = e.getMessage();
    if (r.contains("read_only") || r.contains("timeout")) { retry(req); }
    else throw e;
}

Prevention

When it happens

Trigger: Creating an executable document whose response error type is not a version_conflict: mapper/parsing error, cluster read-only block, malformed JSON body, index-mapping conflict, or a connectivity error surfaced as an error envelope.

Common situations: ES cluster pushed to read_only_allow_indices by disk flood-stage watermark; version skew between client expectations and server; mapping explosion after schema drift; transient 5xx returned as an error blob.

Related errors


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