NationalSecurityAgency/ghidra · error · SQLException

Could not delete executable record

Error message

Could not delete executable record

What it means

Thrown by ExeTable.delete when super.delete(id) returns a row count of 0, meaning no executable row matched the given id for deletion. The id does not correspond to any existing exetable row, so the delete was a no-op. This guards against silently succeeding when the caller expects a record to have been removed.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/tables/ExeTable.java:137

		selectByNameStatement.close();
		super.close();
	}

	@Override
	public void create(Statement st) throws SQLException {
		st.executeUpdate(CREATE_STMT);
	}

	@Override
	public void drop(Statement st) throws SQLException {
		throw new UnsupportedOperationException("ExeTable may not be dropped");
	}

	@Override
	public int delete(long id) throws SQLException {
		int rowcount = super.delete(id);
		if (rowcount == 0) {
			throw new SQLException("Could not delete executable record");
		}
		if (rowcount > 1) {
			throw new SQLException("Problem deleting executable record");
		}
		return rowcount;
	}

	/**
	 * Pulls information out of the given {@link ExecutableRow} object into the given
	 * {@link ResultSet}
	 * 
	 * @param pgres the result set
	 * @param res the executable row
	 * @throws SQLException if there is a problem parsing the result set
	 */
	protected static void extractExecutableRow(ResultSet pgres, ExecutableRow res)
		throws SQLException {
		res.rowid = pgres.getInt(1);

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Check that the executable id exists (querySingleExecutableId) before deleting, or treat a 0-row delete as already-gone rather than an error.
  2. Confirm the id originated from the same database you are deleting from.
  3. Guard against double-deletes by tracking which ids have been removed.
  4. If the id type is mismatched (long vs int), verify the column type matches the setInt cast used internally.

Example fix

// before
table.delete(exeId); // throws if id absent

// after
try {
    table.delete(exeId);
} catch (SQLException e) {
    if (e.getMessage().contains("Could not delete")) {
        // already absent; treat as success for idempotent cleanup
        log.fine("Executable already removed: " + exeId);
    } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check existence before deleting to make double-deletes explicit.
if (exeTable.querySingleExecutableId(id) == null) {
    log.fine("Executable already absent: " + id);
    return; // treat as success
}

Try / catch

try {
    exeTable.delete(id);
} catch (SQLException e) {
    if (e.getMessage().contains("Could not delete")) {
        // idempotent: already gone
        log.fine("Executable already removed: " + id);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling ExeTable.delete(id) with an id that does not exist in exetable. The executable was already deleted, the id is from another database, or the row never existed. Also possible if the delete WHERE clause did not match due to type mismatch (the id is cast to int).

Common situations: Attempting to delete an executable that was already removed (double delete). Using an id resolved from a stale or different database session. Referential cleanup scripts that assume ids still exist. Race where another process deletes the row first.

Related errors


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