NationalSecurityAgency/ghidra · error · LSHException

Overwriting existing executable id

Error message

Overwriting existing executable id

What it means

Thrown by DescriptionManager.newExecutableRecord when an executable with the same md5 and matching metadata already exists, but both the existing and new records carry non-null, differing row ids. Same binary, same metadata, but conflicting database row ids — the manager refuses to overwrite the existing id, as that would break references to the stored row.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java:235

	 * @param arc is the architecture of the executable
	 * @param dt is the date (of ingest)
	 * @param repo is the repository containing the executable
	 * @param path is the path (within the repo) to the executable
	 * @param id is the database (row) is associated with the executable (may be null)
	 * @return the new ExecutableRecord object
	 * @throws LSHException if attributes are invalid, or the executable 
	 *     already exists with different metadata
	 */
	public ExecutableRecord newExecutableRecord(String md5, String enm, String cnm, String arc,
			Date dt, String repo, String path, RowKey id) throws LSHException {
		ExecutableRecord newexe = new ExecutableRecord(md5, enm, cnm, arc, dt, id, repo, path);
		if (!exerec.add(newexe)) {
			ExecutableRecord oldexe = exerec.floor(newexe);
			if (oldexe.compareMetadata(newexe) != 0) {
				throw new LSHException("Duplicate md5 hash, different metadata");
			}
			if ((oldexe.getRowId() != null) && (id != null) && (!oldexe.getRowId().equals(id))) {
				throw new LSHException("Overwriting existing executable id");
			}
			newexe = oldexe;
		}
		return newexe;
	}

	/**
	 * Create a new "library" executable in the container.
	 * Functions in this container (will) have no body or address
	 * @param enm is the name of the library
	 * @param arc is the architecture of the library
	 * @param id is the database id associated with the library (may be null)
	 * @return the new ExecutableRecord object
	 * @throws LSHException if attributes are invalid or the
	 *   library already exists with different metadata
	 */
	public ExecutableRecord newExecutableLibrary(String enm, String arc, RowKey id)
			throws LSHException {

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Before re-adding, reuse the existing ExecutableRecord (and its row id) instead of supplying a new id — pass the existing id or null.
  2. When merging DescriptionManagers, canonicalize each md5 to a single row id and update all references.
  3. Pass null for the id parameter when you want the manager to keep the existing record's id.
  4. Audit the pipeline to ensure a given md5 is only persisted once with one stable id.

Example fix

// before
descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, newId);
// throws: existing record already has a different id

// after
ExecutableRecord existing = descMgr.getExecutableRecord(md5);
RowKey idToUse = (existing != null) ? existing.getRowId() : newId;
// pass null to preserve existing id, or the canonical id
descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, existing != null ? null : newId);
Defensive patterns

Strategy: validation

Validate before calling

// Reuse the existing row id instead of passing a conflicting new one.
ExecutableRecord existing = descMgr.findExecutableByMd5(md5);
RowKey idToPass = (existing != null && existing.getRowId() != null) ? null : id;
return descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, idToPass);

Try / catch

try {
    return descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, id);
} catch (LSHException e) {
    if (e.getMessage().contains("Overwriting existing executable id")) {
        // retry preserving the existing id
        ExecutableRecord ex = descMgr.findExecutableByMd5(md5);
        return descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, null);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling newExecutableRecord where the md5+metadata match an existing record, oldexe.getRowId() is non-null, the new id is non-null, and the two ids are not equal. Occurs when loading the same executable from two database sources/queries that assigned different row ids, or when re-inserting an already-persisted record with a freshly generated id.

Common situations: Merging two DescriptionManagers loaded from different databases where the same binary got different row ids. Re-describing a binary after it was already inserted, passing a new generated id while the old one is retained. Inconsistent id tracking in an ingest pipeline that re-assigns ids.

Related errors


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