skylot/jadx · error · JadxRuntimeException

Duplicate class:

Error message

Duplicate class: 

What it means

Thrown by ClsSet.loadFrom when two classes in the input root have the same raw type name. The method iterates root.getClasses(true), builds a map keyed by ArgType.getObject() (the class's internal raw name), and throws if names.put returns a non-null previous value. This indicates a genuine collision in the input set being exported to the classpath format.

Solutions

  1. Identify and remove the duplicate class from one of the input sources before running ClsSet.loadFrom.
  2. For multi-dex inputs, use jadx's deduplication options or check if a class is legitimately duplicated and exclude the redundant copy.
  3. Inspect the class name in the error message and search the input APKs/JARs for that class to locate both sources.
  4. If this occurs during a custom jadx-based tool, filter the root's class list to unique names before calling loadFrom.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-deduplicate classes by raw name before calling loadFrom
List<ClassNode> classes = root.getClasses(true);
Map<String, ClassNode> deduped = new LinkedHashMap<>();
for (ClassNode cls : classes) {
    String name = cls.getClassInfo().getType().getObject();
    deduped.putIfAbsent(name, cls);
}
// Check for duplicates
if (deduped.size() < classes.size()) {
    throw new IllegalStateException("Duplicate class names detected in input");
}

Try / catch

try {
    clsSet.loadFrom(root);
} catch (JadxRuntimeException e) {
    if (e.getMessage().startsWith("Duplicate class:")) {
        String className = e.getMessage().substring("Duplicate class: ".length());
        LOG.warn("Skipping duplicate class: {}", className);
        // remove duplicate and retry
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling ClsSet.loadFrom(root) where the RootNode contains two ClassNodes resolving to the same raw class name (e.g., 'com.example.Foo' appearing in two different input files). This can happen with multi-dex inputs that contain duplicate class definitions, or when processing an APK with overlapping library JARs.

Common situations: Decompiling a multi-dex APK where the same class is duplicated across dex files (sometimes due to build tooling issues). Running the classpath export (e.g., via jadx-cli export or internal ClsSet.save) on an input with bundled duplicate libraries. Obfuscated inputs where renaming causes name collisions after processing.

Related errors


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

Appendix: source

Thrown at jadx-core/src/main/java/jadx/core/clsp/ClsSet.java:102

			int methodsCount = Stream.of(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum();
			LOG.debug("Clst file loaded in {}ms, android api: {}, classes: {}, methods: {}",
					time, androidApiLevel, classes.length, methodsCount);
		}
	}

	public void loadFrom(RootNode root) {
		List<ClassNode> list = root.getClasses(true);
		Map<String, ClspClass> names = new HashMap<>(list.size());
		int k = 0;
		for (ClassNode cls : list) {
			ArgType clsType = cls.getClassInfo().getType();
			String clsRawName = clsType.getObject();
			cls.load();

			ClspClassSource source = getClspClassSource(cls);
			ClspClass nClass = new ClspClass(clsType, k, cls.getAccessFlags().rawValue(), source);
			if (names.put(clsRawName, nClass) != null) {
				throw new JadxRuntimeException("Duplicate class: " + clsRawName);
			}
			k++;
			nClass.setTypeParameters(cls.getGenericTypeParameters());
			nClass.setMethods(getMethodsDetails(cls));
		}
		classes = new ClspClass[k];
		k = 0;
		for (ClassNode cls : list) {
			ClspClass nClass = getCls(cls, names);
			if (nClass == null) {
				throw new JadxRuntimeException("Missing class: " + cls);
			}
			nClass.setParents(makeParentsArray(cls));
			classes[k] = nClass;
			k++;
		}
	}

View on GitHub (pinned to e738a26571)