NationalSecurityAgency/ghidra · error · InvalidInputException

Didn't remove external library {libName}

Error message

Didn't remove external library {libName}

What it means

Thrown as an InvalidInputException by removeExternalLibrary when ExternalManager.removeExternalLibrary(libName) returns false, meaning the external library could not be removed from the program even though it appeared to exist and was empty. The companion earlier check already guards the 'not empty' case, so this throw specifically signals the removal API itself failed (the library was absent or the model rejected the mutation). Callers are expected to treat it as a hard failure of the merge step rather than a transient condition.

Source

Thrown at Ghidra/Features/Base/src/main/java/ghidra/app/merge/listing/ExternalProgramMerger.java:585

	}

	/**
	 * Removes the indicated external library from the indicated program version
	 * if it is empty.
	 * @param program the program
	 * @param libName the external library name
	 * @throws InvalidInputException if there is no such enterrnal library in the program
	 */
	private void removeExternalLibrary(Program program, String libName)
			throws InvalidInputException {
		ExternalManager extMgr = program.getExternalManager();
		ExternalLocationIterator iter = extMgr.getExternalLocations(libName);
		if (iter.hasNext()) {
			throw new InvalidInputException(
				"Didn't remove external library " + libName + " since it isn't empty.");
		}
		if (!extMgr.removeExternalLibrary(libName)) {
			throw new InvalidInputException("Didn't remove external library " + libName);
		}
	}

	/**
	 * Determines whether the latest external program name and my external program name are equals.
	 * @param latestName the latest external program name or null.
	 * @param myName my external program name or null.
	 * @return true if the names are equal.
	 */
	private boolean same(String latestName, String myName) {
		return SystemUtilities.isEqual(latestName, myName);
	}

	/**
	 * Performs a manual merge of external program conflicts.
	 * @param chosenConflictOption ASK_USER means interactively resolve conflicts.
	 * JUnit testing also allows setting this to LATEST, MY, or ORIGINAL to force
	 * selection of a particular version change.

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Re-check extMgr.hasExternalLibrary(libName) immediately before calling removeExternalLibrary, and skip removal if it is already gone.
  2. Ensure the result program is checked out / writable and that no other thread is mutating the ExternalManager during the merge.
  3. Wrap the call in try/catch(InvalidInputException) and log + continue if removal is non-fatal to your merge pass.
  4. If the library is genuinely empty but removal fails, inspect the program's external locations for stale/dangling entries blocking the delete.

Example fix

// before
private void removeExternalLibrary(Program program, String libName) throws InvalidInputException {
    ExternalManager extMgr = program.getExternalManager();
    if (extMgr.getExternalLocations(libName).hasNext()) {
        throw new InvalidInputException("Didn't remove external library " + libName + " since it isn't empty.");
    }
    if (!extMgr.removeExternalLibrary(libName)) {
        throw new InvalidInputException("Didn't remove external library " + libName);
    }
}

// after — guard the already-removed case
private void removeExternalLibrary(Program program, String libName) throws InvalidInputException {
    ExternalManager extMgr = program.getExternalManager();
    if (!extMgr.hasExternalLibrary(libName)) {
        return; // already gone, nothing to do
    }
    if (extMgr.getExternalLocations(libName).hasNext()) {
        throw new InvalidInputException("Didn't remove external library " + libName + " since it isn't empty.");
    }
    if (!extMgr.removeExternalLibrary(libName)) {
        throw new InvalidInputException("Didn't remove external library " + libName);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

ExternalManager extMgr = program.getExternalManager();
if (!extMgr.hasExternalLibrary(libName)) {
    return; // nothing to remove
}
if (extMgr.getExternalLocations(libName).hasNext()) {
    // library not empty — do not call remove
    return;
}

Type guard

boolean removable = program.getExternalManager().hasExternalLibrary(libName)
    && !program.getExternalManager().getExternalLocations(libName).hasNext();

Try / catch

try {
    removeExternalLibrary(program, libName);
} catch (InvalidInputException e) {
    Msg.warn(this, "Skipping external library removal: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling removeExternalLibrary(program, libName) where extMgr.getExternalLocations(libName).hasNext() is false (empty) but extMgr.removeExternalLibrary(libName) still returns false — e.g. the library name was removed by a concurrent transaction, the name does not actually exist in the ExternalManager, or the underlying program is read-only/immutable during the merge.

Common situations: Version-control merge of two Ghidra program checkouts where both sides deleted or renamed the same external library; running the listing/externals merge on a program opened read-only; an earlier merge phase already removed the library so a second pass finds nothing to remove.

Related errors


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