pxb1988/dex2jar · error · RuntimeException
Can't merge and
Error message
Can't merge ${a} and ${b} What it means
TypeClass represents value-type abstractions (zero/one/boolean, int/float, long, etc.) in dex2jar's IR. merge0 computes the least upper bound of two type classes; if either operand is JD (the 'double' class, which cannot combine with any other), no sensible result exists and the library throws this RuntimeException. It signals that the type-inference pass encountered two conflicting type sets during local variable merging.
Solutions
- Inspect the IrMethod locals and fix the pass that assigned double (JD) to a register used as another type
- Re-run on non-obfuscated DEX to confirm the input is the cause; preprocess with a dex deobfuscator
- If intentional, extend merge0's table or catch RuntimeException around the transform pipeline
- Report the input DEX as a dex2jar translation bug with the failing method
Example fix
// before (library)
if (a == JD || b == JD) {
throw new RuntimeException("Can't merge " + a + " and " + b);
}
// after (caller guard)
TypeClass m;
try { m = TypeClass.merge(a, b); }
catch (RuntimeException e) { m = TypeClass.UNKNOWN; } Defensive patterns
Strategy: try-catch
Validate before calling
if (a == TypeClass.JD || b == TypeClass.JD) {
throw new IllegalArgumentException("cannot merge double class with " + a + "/" + b);
}
TypeClass merged = TypeClass.merge(a, b); Type guard
boolean mergeable(TypeClass t) { return t != TypeClass.JD; } Try / catch
try {
merged = TypeClass.merge(a, b);
} catch (RuntimeException e) {
if (!e.getMessage().startsWith("Can't merge ")) throw e;
merged = TypeClass.UNKNOWN; // or re-run with corrected types
} Prevention
- Keep each IR local assigned a single consistent type across branches
- Run type fixing/SSA passes before any TypeClass.merge use
- When handling obfuscated DEX, normalize register types first
When it happens
Trigger: Calling TypeClass.merge (or merge0 directly) with one argument being TypeClass.JD while the other is any type class that yields no entry in the merge table (e.g. merging a double-typed value with an int/float/object value).
Common situations: Deobfuscating or translating malicious/obfuscated DEX where the same register is written with a double in one branch and a different type in another; bugs in custom transformers that assign types inconsistently before Cfg/type analysis.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
AI-assisted analysis of pxb1988/dex2jar@b5bda4fb49 (2026-09-08).
Data as JSON: /api/errors/6af5ae839ab10c3d.
Report an issue: GitHub.
Appendix: source
Thrown at dex-ir/src/main/java/com/googlecode/dex2jar/ir/TypeClass.java:126
} else if (clz.fixed) {
return clz;
} else { // both not fixed
return merge0(thizCls, clz);
}
}
}
/**
* X ZIL ZIFL ZIF ZI IF
* ZIL X ZIL ZI ZI I
* ZIFL ZIL X ZIF ZI IF
* ZIF ZI ZIF X ZI IF
* ZI ZI ZI ZI X I
* IF I IF IF I X
*/
private static TypeClass merge0(TypeClass a, TypeClass b) {
if (a == JD || b == JD) {
throw new RuntimeException("Can't merge " + a + " and " + b);
}
switch (a) {
case ZIL:
switch (b) {
case ZIFL:
return ZIL;
case IF:
return INT;
case ZIF:
case ZI:
return ZI;
default:
}
case ZIFL:
return b;
case IF:
switch (b) {
case ZIL:View on GitHub (pinned to b5bda4fb49)