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

  1. Check for duplicate plugin JARs on the classpath or in the plugins directory — remove the older or unwanted copy.
  2. If using --plugins or programmatic registration, ensure the plugin is not also auto-loaded via ServiceLoader.
  3. Inspect PluginContext.getPluginId() for both plugins (the error message includes the plugin context toString) to identify which two are colliding.
  4. Use JadxPluginManager.unload(pluginId) before re-registering if intentional replacement is needed.
  5. 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

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


AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14). Data as JSON: /api/errors/4bca7a331e6255e4. Report an issue: GitHub.