{"record":{"id":"77645908551eed1f","repo":"NationalSecurityAgency/ghidra","slug":"didn-t-remove-external-library-libname","errorCode":null,"errorMessage":"Didn't remove external library {libName}","messagePattern":"Didn't remove external library (.+?)","errorType":"validation","errorClass":"InvalidInputException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/Base/src/main/java/ghidra/app/merge/listing/ExternalProgramMerger.java","lineNumber":585,"sourceCode":"\t}\n\n\t/**\n\t * Removes the indicated external library from the indicated program version\n\t * if it is empty.\n\t * @param program the program\n\t * @param libName the external library name\n\t * @throws InvalidInputException if there is no such enterrnal library in the program\n\t */\n\tprivate void removeExternalLibrary(Program program, String libName)\n\t\t\tthrows InvalidInputException {\n\t\tExternalManager extMgr = program.getExternalManager();\n\t\tExternalLocationIterator iter = extMgr.getExternalLocations(libName);\n\t\tif (iter.hasNext()) {\n\t\t\tthrow new InvalidInputException(\n\t\t\t\t\"Didn't remove external library \" + libName + \" since it isn't empty.\");\n\t\t}\n\t\tif (!extMgr.removeExternalLibrary(libName)) {\n\t\t\tthrow new InvalidInputException(\"Didn't remove external library \" + libName);\n\t\t}\n\t}\n\n\t/**\n\t * Determines whether the latest external program name and my external program name are equals.\n\t * @param latestName the latest external program name or null.\n\t * @param myName my external program name or null.\n\t * @return true if the names are equal.\n\t */\n\tprivate boolean same(String latestName, String myName) {\n\t\treturn SystemUtilities.isEqual(latestName, myName);\n\t}\n\n\t/**\n\t * Performs a manual merge of external program conflicts.\n\t * @param chosenConflictOption ASK_USER means interactively resolve conflicts.\n\t * JUnit testing also allows setting this to LATEST, MY, or ORIGINAL to force\n\t * selection of a particular version change.","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/Base/src/main/java/ghidra/app/merge/listing/ExternalProgramMerger.java#L567-L603","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-check extMgr.hasExternalLibrary(libName) immediately before calling removeExternalLibrary, and skip removal if it is already gone.","Ensure the result program is checked out / writable and that no other thread is mutating the ExternalManager during the merge.","Wrap the call in try/catch(InvalidInputException) and log + continue if removal is non-fatal to your merge pass.","If the library is genuinely empty but removal fails, inspect the program's external locations for stale/dangling entries blocking the delete."],"exampleFix":"// before\nprivate void removeExternalLibrary(Program program, String libName) throws InvalidInputException {\n    ExternalManager extMgr = program.getExternalManager();\n    if (extMgr.getExternalLocations(libName).hasNext()) {\n        throw new InvalidInputException(\"Didn't remove external library \" + libName + \" since it isn't empty.\");\n    }\n    if (!extMgr.removeExternalLibrary(libName)) {\n        throw new InvalidInputException(\"Didn't remove external library \" + libName);\n    }\n}\n\n// after — guard the already-removed case\nprivate void removeExternalLibrary(Program program, String libName) throws InvalidInputException {\n    ExternalManager extMgr = program.getExternalManager();\n    if (!extMgr.hasExternalLibrary(libName)) {\n        return; // already gone, nothing to do\n    }\n    if (extMgr.getExternalLocations(libName).hasNext()) {\n        throw new InvalidInputException(\"Didn't remove external library \" + libName + \" since it isn't empty.\");\n    }\n    if (!extMgr.removeExternalLibrary(libName)) {\n        throw new InvalidInputException(\"Didn't remove external library \" + libName);\n    }\n}","handlingStrategy":"validation","validationCode":"ExternalManager extMgr = program.getExternalManager();\nif (!extMgr.hasExternalLibrary(libName)) {\n    return; // nothing to remove\n}\nif (extMgr.getExternalLocations(libName).hasNext()) {\n    // library not empty — do not call remove\n    return;\n}","typeGuard":"boolean removable = program.getExternalManager().hasExternalLibrary(libName)\n    && !program.getExternalManager().getExternalLocations(libName).hasNext();","tryCatchPattern":"try {\n    removeExternalLibrary(program, libName);\n} catch (InvalidInputException e) {\n    Msg.warn(this, \"Skipping external library removal: \" + e.getMessage());\n}","preventionTips":["Check hasExternalLibrary and emptiness before removeExternalLibrary.","Run the merge with the result program writable and no concurrent mutations.","Make removal idempotent — tolerate the already-removed case."],"tags":["ghidra","merge","external-library","invalid-input"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}