skylot/jadx · error · IllegalArgumentException
Duplicate plugin id: {}, class {}
Error message
Duplicate plugin id: {}, class {} What it means
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.
Source
Thrown at jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java:84
}
LOG.debug("Register plugin: {}", addedPlugin.getPluginId());
resolve();
}
private @Nullable PluginContext addPlugin(JadxPlugin plugin, VerifyRequiredVersion verifyRequiredVersion) {
PluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);
if (disabledPlugins.contains(pluginContext.getPluginId())) {
return null;
}
String requiredJadxVersion = pluginContext.getPluginInfo().getRequiredJadxVersion();
if (!verifyRequiredVersion.isCompatible(requiredJadxVersion)) {
LOG.warn("Plugin '{}' not loaded: requires '{}' jadx version which it is not compatible with current: {}",
pluginContext, requiredJadxVersion, verifyRequiredVersion.getJadxVersion());
return null;
}
LOG.debug("Loading plugin: {}", pluginContext);
if (!allPlugins.add(pluginContext)) {
throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass());
}
addPluginListeners.forEach(l -> l.accept(pluginContext));
return pluginContext;
}
public boolean unload(String pluginId) {
boolean result = allPlugins.removeIf(context -> {
if (context.getPluginId().equals(pluginId)) {
LOG.debug("Unload plugin: {}", pluginId);
return true;
}
return false;
});
resolve();
return result;
}
public SortedSet<PluginContext> getAllPluginContexts() {View on GitHub (pinned to e738a26571)
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').
Example fix
// before — same plugin loaded via classpath and explicit register
manager.load(pluginLoader); // loads 'com.example.foo' from classpath
manager.register(myFooPlugin); // same id 'com.example.foo' -> crash
// after — unload first, or avoid double-loading
manager.load(pluginLoader);
// do not register again; or:
manager.unload("com.example.foo");
manager.register(myFooPlugin); Defensive patterns
Strategy: validation
Validate before calling
// Before registering, check if a plugin with the same ID is already loaded
String pluginId = plugin.getPluginInfo().getPluginId();
boolean alreadyLoaded = manager.getAllPluginContexts().stream()
.anyMatch(ctx -> ctx.getPluginId().equals(pluginId));
if (alreadyLoaded) {
manager.unload(pluginId); // unload first if replacement is intended
}
manager.register(plugin); Try / catch
try {
manager.register(plugin);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Duplicate plugin id")) {
LOG.warn("Plugin already loaded, skipping: {}", plugin.getPluginInfo().getPluginId());
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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().
Related errors
- Null option descriptions in plugin id: {}
- Plugin option name should start with plugin id: '{}', option
- Plugin option description not set, plugin: {}
- Plugin option values is null, option: {}, plugin: {}
- Ordering pass not found: {}, listed in 'runAfter' of pass: {
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/4bca7a331e6255e4.
Report an issue: GitHub.