NationalSecurityAgency/ghidra · critical · SQLException
Unexpected updated row count: {}
Error message
Unexpected updated row count: {} What it means
Thrown as an SQLException by H2VectorTable.updateVector() when an UPDATE on vec_hash affects more than one row. The h2_vectable has a unique index on vec_hash, so an UPDATE matching by vec_hash should affect at most one row. A row count greater than 1 indicates the unique constraint on vec_hash was violated (duplicate hash values exist), which is a data integrity violation.
Source
Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/H2VectorTable.java:220
public long updateVector(LSHVector vec, int countDiff) throws SQLException {
if (countDiff <= 0) {
throw new IllegalArgumentException("Invalid countDiff: " + countDiff);
}
// TODO: it may be possible to optimize the technique employed here
PreparedStatement s = update_by_hash_stmt.prepareIfNeeded(() -> db.prepareStatement(
"UPDATE " + TABLE_NAME + " SET count = count + ? WHERE vec_hash = ?"));
long vecHash = vec.calcUniqueHash();
s.setInt(1, countDiff);
s.setLong(2, vecHash);
int rc = s.executeUpdate();
if (rc == 0) {
return insert(countDiff, vec);
}
if (rc > 1) {
throw new SQLException("Unexpected updated row count: " + rc);
}
s = select_id_by_hash_stmt.prepareIfNeeded(() -> db
.prepareStatement("SELECT id, count FROM " + TABLE_NAME + " WHERE vec_hash = ?"));
s.setLong(1, vecHash);
long id;
int count;
try (ResultSet rs = s.executeQuery()) {
if (!rs.next()) {
throw new SQLException("Unknown vector hash");
}
id = rs.getLong(1);
count = rs.getInt(2);
}
vectorStore.update(
new VectorStoreEntry(id, vec, count, vectorFactory.getSelfSignificance(vec)));
return id;View on GitHub (pinned to d5f144c24d)
Solutions
- Query the table for duplicate vec_hash values: 'SELECT vec_hash, COUNT(*) FROM h2_vectable GROUP BY vec_hash HAVING COUNT(*) > 1' to identify and resolve duplicates.
- If duplicates exist, remove or merge them so each vec_hash is unique.
- Verify the unique index h2_vectable_index exists on the table: 'SELECT * FROM information_schema.indexes WHERE index_name = h2_vectable_index'.
- If the index was dropped, recreate it.
- If hash collisions are the root cause, investigate calcUniqueHash() for collisions and consider rebuilding the database.
Example fix
// before
int rc = s.executeUpdate();
if (rc > 1) {
throw new SQLException("Unexpected updated row count: " + rc);
}
// after (detect and report duplicates before failing)
int rc = s.executeUpdate();
if (rc > 1) {
throw new SQLException(String.format(
"Data integrity violation: %d rows share vec_hash %d; " +
"run: SELECT id FROM h2_vectable WHERE vec_hash = %d", rc, vecHash, vecHash));
} Defensive patterns
Strategy: validation
Validate before calling
// Check for duplicate vec_hash values before update operations
try (Statement st = db.createStatement();
ResultSet rs = st.executeQuery(
"SELECT vec_hash, COUNT(*) as cnt FROM " + TABLE_NAME +
" GROUP BY vec_hash HAVING COUNT(*) > 1")) {
if (rs.next()) {
throw new SQLException(String.format(
"Duplicate vec_hash detected: %d appears %d times",
rs.getLong(1), rs.getInt(2)));
}
} Try / catch
try {
return vectorTable.updateVector(vec, countDiff);
} catch (SQLException e) {
if (e.getMessage().startsWith("Unexpected updated row count:")) {
// Data integrity violation: resolve duplicates first
long hash = vec.calcUniqueHash();
resolveDuplicateVecHashes(db, hash);
return vectorTable.updateVector(vec, countDiff); // retry
}
throw e;
} Prevention
- Periodically check for duplicate vec_hash values in production databases.
- Verify the unique index h2_vectable_index exists after any schema operation.
- Do not insert vectors bypassing the normal insert/updateVector path.
- If hash collisions occur, investigate calcUniqueHash() and consider rebuilding the database.
- Treat this as a critical data integrity issue requiring immediate investigation.
When it happens
Trigger: Occurs in updateVector() when 'UPDATE h2_vectable SET count = count + ? WHERE vec_hash = ?' returns rc > 1. This means multiple rows share the same vec_hash value despite the unique index h2_vectable_index. This can happen if: the unique index was dropped or corrupted, two different LSHVectors produce a hash collision in calcUniqueHash(), or a prior insert bypassed the unique constraint.
Common situations: Hash collision in calcUniqueHash() where two genuinely different vectors produce the same hash (extremely rare but possible). The unique index was manually dropped. Database corruption caused duplicate entries. A bug in a prior version allowed duplicate vec_hash values to be inserted.
Related errors
- Bad vector table rowid
- Insert failed for vector table
- Bad vector table rowid
- Unable to obtain vector id for insert
- {}
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/8c260946e1aec9a0.
Report an issue: GitHub.