NationalSecurityAgency/ghidra · warning · DatabaseNonFatalException

Already inserted

Error message

Already inserted

What it means

After executable deduplication, if `pickout_storedfuncs` is set and `markPreviouslyStoredFunctions` returns false, the method throws DatabaseNonFatalException("Already inserted"). A false return means every function in the batch was already present -- nothing new remains to insert.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/AbstractSQLFunctionDatabase.java:1079

					}
				}
				else {
					input.setExeRowId(erec, tmp.getRowId());
				}
				input.setExeAlreadyStored(erec);
				// Just because we've seen a library before doesn't mean we should stop function insertion
				// Libraries are likely to be partially inserted multiple times
				if (!erec.isLibrary()) {
					throw new DatabaseNonFatalException(
						erec.getNameExec() + " is already ingested");
				}
				pickout_storedfuncs = true; // At least one executable has previously inserted functions
			}
		}

		if (pickout_storedfuncs) {
			if (!markPreviouslyStoredFunctions(input, input.listAllFunctions())) {
				throw new DatabaseNonFatalException("Already inserted");
			}
		}
	}

	/**
	 * Do the final work of inserting new ExecutableRecords into the database. This function
	 * assumes testExecutableDuplication has already run and marked previously ingested records
	 * 
	 * @param input the executable descriptor
	 * @throws SQLException if database records cannot be inserted
	 */
	private void commitExecutables(DescriptionManager input) throws SQLException {
		Iterator<ExecutableRecord> iter = input.getExecutableRecordSet().iterator();
		while (iter.hasNext()) {
			ExecutableRecord erec = iter.next();
			if (erec.isAlreadyStored()) {
				continue;
			}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Treat DatabaseNonFatalException("Already inserted") as expected and skip the batch.
  2. Track ingested-function inventory to avoid submitting fully-duplicate batches.
  3. Design ingest as idempotent and tolerant of this non-fatal result.

Example fix

// before: batch insert repeatedly throws non-fatal, aborting the run
// after: catch at the batch boundary and continue
try { db.insert(manager); }
catch (DatabaseNonFatalException e) {
    log.info("batch already inserted: {}", e.getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before insert, check whether all functions in the batch are already stored.
boolean anyNew = false;
for (FunctionDescription fd : manager.listAllFunctions()) {
    if (!isAlreadyStored(fd)) { anyNew = true; break; }
}
if (!anyNew) log.info("batch fully already-inserted; skipping");

Try / catch

try {
    db.insert(manager);
} catch (DatabaseNonFatalException e) {
    if ("Already inserted".equals(e.getMessage())) {
        // entire batch was present -- expected for re-runs, continue
        log.info("batch already inserted: {}", e.getMessage());
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Inserting a batch of functions whose signatures are all already stored (prior ingest of the same executable's functions). `markPreviouslyStoredFunctions` found zero new functions and returned false.

Common situations: Re-running the same ingest; functions inserted earlier under a library context that overlaps the current set; idempotent retries.

Related errors


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