{"record":{"id":"4bca7a331e6255e4","repo":"skylot/jadx","slug":"duplicate-plugin-id-class","errorCode":null,"errorMessage":"Duplicate plugin id: {}, class {}","messagePattern":"Duplicate plugin id: (.+?), class (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java","lineNumber":84,"sourceCode":"\t\t}\n\t\tLOG.debug(\"Register plugin: {}\", addedPlugin.getPluginId());\n\t\tresolve();\n\t}\n\n\tprivate @Nullable PluginContext addPlugin(JadxPlugin plugin, VerifyRequiredVersion verifyRequiredVersion) {\n\t\tPluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);\n\t\tif (disabledPlugins.contains(pluginContext.getPluginId())) {\n\t\t\treturn null;\n\t\t}\n\t\tString requiredJadxVersion = pluginContext.getPluginInfo().getRequiredJadxVersion();\n\t\tif (!verifyRequiredVersion.isCompatible(requiredJadxVersion)) {\n\t\t\tLOG.warn(\"Plugin '{}' not loaded: requires '{}' jadx version which it is not compatible with current: {}\",\n\t\t\t\t\tpluginContext, requiredJadxVersion, verifyRequiredVersion.getJadxVersion());\n\t\t\treturn null;\n\t\t}\n\t\tLOG.debug(\"Loading plugin: {}\", pluginContext);\n\t\tif (!allPlugins.add(pluginContext)) {\n\t\t\tthrow new IllegalArgumentException(\"Duplicate plugin id: \" + pluginContext + \", class \" + plugin.getClass());\n\t\t}\n\t\taddPluginListeners.forEach(l -> l.accept(pluginContext));\n\t\treturn pluginContext;\n\t}\n\n\tpublic boolean unload(String pluginId) {\n\t\tboolean result = allPlugins.removeIf(context -> {\n\t\t\tif (context.getPluginId().equals(pluginId)) {\n\t\t\t\tLOG.debug(\"Unload plugin: {}\", pluginId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tresolve();\n\t\treturn result;\n\t}\n\n\tpublic SortedSet<PluginContext> getAllPluginContexts() {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java#L66-L102","documentation":"Thrown as IllegalArgumentException by JadxPluginManager.addPlugin() when allPlugins.add(pluginContext) returns false. allPlugins is a TreeSet<PluginContext> sorted by PluginContext's compareTo, so add() returns false if an equal context already exists (compareTo returns 0). Two plugins with the same plugin ID (or same sort key) are considered duplicates.","triggerScenarios":"Two different JadxPlugin instances produce PluginContext objects that compare as equal — typically because they share the same pluginId. This happens when the same plugin class is loaded twice (e.g. present on the classpath and also explicitly registered), or when two distinct plugin implementations use the same plugin ID string.","commonSituations":"Including a plugin JAR in the classpath that is also auto-discovered via ServiceLoader. Bundling two versions of the same plugin. Registering a plugin via JadxPluginManager.register() that was already loaded by the plugin loader. Custom plugin development where two plugins accidentally declare the same getPluginId().","solutions":["Check for duplicate plugin JARs on the classpath or in the plugins directory — remove the older or unwanted copy.","If using --plugins or programmatic registration, ensure the plugin is not also auto-loaded via ServiceLoader.","Inspect PluginContext.getPluginId() for both plugins (the error message includes the plugin context toString) to identify which two are colliding.","Use JadxPluginManager.unload(pluginId) before re-registering if intentional replacement is needed.","If developing a plugin, ensure getPluginInfo().getPluginId() returns a unique, namespaced ID (e.g. 'com.example.myplugin')."],"exampleFix":"// before — same plugin loaded via classpath and explicit register\nmanager.load(pluginLoader); // loads 'com.example.foo' from classpath\nmanager.register(myFooPlugin); // same id 'com.example.foo' -> crash\n\n// after — unload first, or avoid double-loading\nmanager.load(pluginLoader);\n// do not register again; or:\nmanager.unload(\"com.example.foo\");\nmanager.register(myFooPlugin);","handlingStrategy":"validation","validationCode":"// Before registering, check if a plugin with the same ID is already loaded\nString pluginId = plugin.getPluginInfo().getPluginId();\nboolean alreadyLoaded = manager.getAllPluginContexts().stream()\n    .anyMatch(ctx -> ctx.getPluginId().equals(pluginId));\nif (alreadyLoaded) {\n    manager.unload(pluginId); // unload first if replacement is intended\n}\nmanager.register(plugin);","typeGuard":null,"tryCatchPattern":"try {\n    manager.register(plugin);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Duplicate plugin id\")) {\n        LOG.warn(\"Plugin already loaded, skipping: {}\", plugin.getPluginInfo().getPluginId());\n    } else {\n        throw e;\n    }\n}","preventionTips":["Avoid putting the same plugin JAR in both the classpath and the plugins directory.","Use unique, namespaced plugin IDs (e.g. reverse-domain convention).","Call unload() before re-registering if you need to replace a plugin at runtime.","Audit the classpath for duplicate plugin JARs using jar tf or a dependency-tree plugin."],"tags":["jadx","plugins","duplicate-registration","illegal-argument","configuration"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}