NationalSecurityAgency/ghidra · critical · ElasticException

Mismatch in metaid

Error message

Mismatch in metaid

What it means

Internal consistency check in fetchVectorCounts: after a meta/_mget multi-get, each returned document's base64 _id is decoded and compared to the VectorResult.vectorid it should correspond to. A mismatch means the docs came back in a different order or with different ids than requested. ES _mget is supposed to preserve request order, so this indicates corruption, a non-ES backend, or a proxy/gateway that reordered the response.

Source

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

			vecRes = iter1.next();
			buffer.append(", \"");
			Base64Lite.encodeLongBase64(buffer, vecRes.vectorid);
			buffer.append('\"');
		}
		buffer.append(" ] }");
		JsonObject resp =
			connection.executeStatement(ElasticConnection.GET, "meta/_mget", buffer.toString());
		JsonArray docs = (JsonArray) resp.get("docs");
		for (int i = 0; i < maxDocuments; ++i) {
			if (!iter2.hasNext()) {
				break;
			}
			vecRes = iter2.next();
			JsonObject oneResp = (JsonObject) docs.get(i);
			String matchId = oneResp.get("_id").getAsString();
			long matchIdVal = Base64Lite.decodeLongBase64(matchId);
			if (matchIdVal != vecRes.vectorid) {
				throw new ElasticException("Mismatch in metaid");
			}
			JsonElement source = oneResp.get("_source");
			if (ElasticConnection.isNull(source)) {
				throw new ElasticException("meta document does not exist for id=" + matchId);
			}
			long count = ((JsonObject) source).get("count").getAsLong();
			totalCount += count;
			vecRes.hitcount = (int) count;
		}
		return totalCount;
	}

	/**
	 * Fetch vectors in bulk from the database, given a list of VectorResults with the vector ids
	 * The vector documents are queried, then the resulting LSHVector objects are filled
	 * in for the VectorResults by parsing the documents. Two iterators pointing to the same list
	 * of VectorResults are required, one for building the query, one for filling in the LSHVectors.
	 * If no exception is thrown, both iterators are advanced the same number of times.

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Reproduce with a direct curl _mget against ES (bypassing any proxy) to confirm ES itself preserves order; if it does, the proxy is the culprit.
  2. Match the VectorResult list and the docs array by id instead of by position when reshaping results, or re-run the query in smaller batches.
  3. If corruption is confirmed, reindex the affected BSim repository from a known-good source.

Example fix

// before: relies on positional order returned by _mget
// (internal in fetchVectorCounts; no direct caller fix)
// after (defensive, if you wrap the call): retry once, then fall back to single-doc gets
try { return db.fetchVectorCounts(...); }
catch (ElasticException e) { if (e.getMessage().equals("Mismatch in metaid")) return fetchVectorCountsOneByOne(...); throw e; }
Defensive patterns

Strategy: fallback

Validate before calling

// Cannot be pre-validated from outside; it is an internal ordering assertion.
// Mitigate by issuing the multi-get directly (bypassing any reordering proxy) for verification:
public static boolean mgetPreservesOrder(String url, String index) throws Exception {
    HttpURLConnection h = (HttpURLConnection) URI.create(url + "/" + index + "/_mget").toURL().openConnection();
    h.setRequestMethod("GET"); h.setDoOutput(true);
    try (Writer w = new OutputStreamWriter(h.getOutputStream())) { w.write("{\"ids\":[\"AAAB\",\"AAAC\"]}"); }
    JsonObject r = JsonParser.parseReader(new InputStreamReader(h.getInputStream())).getAsJsonObject();
    return ((JsonArray) r.get("docs")).get(0).getAsJsonObject().get("_id").getAsString().equals("AAAB");
}

Try / catch

try {
    return db.fetchVectorCounts(it1, it2, n);
} catch (ElasticException e) {
    if (!e.getMessage().equals("Mismatch in metaid")) throw e;
    // fall back to per-id single gets, which are not order-dependent
    return fetchVectorCountsSingly(it1, n);
}

Prevention

When it happens

Trigger: fetchVectorCounts iterating VectorResults after a meta/_mget where docs[i]._id decodes to a value != the i-th requested vectorid. Caused by a man-in-the-middle/proxy reordering docs, a non-ES service answering _mget, a buggy ES plugin, or index corruption returning wrong ids.

Common situations: ES behind a gateway that rewrites/reorders multi-get responses; talking to an OpenSearch/plugin variant whose _mget semantics differ; rare index corruption after a hardware/replication fault.

Related errors


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