pxb1988/dex2jar · error · RuntimeException
stack not balanced
Error message
stack not balanced
What it means
Thrown by J2IRConverter.merge() while abstractly interpreting JVM bytecode to build the data-flow frames used for Java-to-IR conversion. When two control-flow paths converge on a block that already has a frame, the operand stack depths must match; if the incoming stack size differs from the existing frame's stack size, the converter declares the stack unbalanced and aborts, because it cannot build a consistent single-assignment relation for the operands.
Solutions
- Identify the offending method/class from the surrounding stack trace and recompile it from source with a standard compiler so stack maps are balanced
- Remove or soften the obfuscation (e.g. disable branch/control-flow obfuscation options) and retry the conversion
- Ensure the input class file is not corrupted and matches a supported ASM class-file version (update dex-translator/ASM dependencies)
- Patch J2IRConverter.merge to pad/trim stacks heuristically if you control the build and must accept such bytecode
Example fix
// before: converting obfuscated jar
dexTranslator.convert(archiveIn, archiveOut);
// after: pre-strip control-flow obfuscation, e.g. recompile source or run a deobfuscator first
jar2dex.convert(new File("input-obfuscated.jar"), new File("out-dex.zip")); // feed deobfuscated jar instead Defensive patterns
Strategy: try-catch
Validate before calling
// Prefer pre-verified input; check for branch obfuscation markers before conversion
boolean risky = classFileWasObfuscated || !compiledByStandardCompiler;
if (risky) { runDeobfuscatorOrRecompile(); } Try / catch
try {
dexTranslator.convert(inJar, outDex);
} catch (RuntimeException e) {
if ("stack not balanced".equals(e.getMessage())) {
log.warn("Unbalanced-stack method; recompile or deobfuscate input", e);
// fall back to a pre-desugared/deobfuscated artifact
} else throw e;
} Prevention
- Feed only javac/ecj-compiled classes, not hand-assembled or obfuscated bytecode
- Disable control-flow obfuscation for artifacts destined for jar2dex conversion
- Pin compatible dex-translator and ASM versions
- Log failing classes and rebuild them from source
When it happens
Trigger: Converting a Java class whose bytecode has a jump target reachable from paths with different operand-stack depths — typically hand-written, obfuscated, or bytecode-generated classes (ASM/bytecode manipulation tools) that leave values on the stack across branches; javac-compiled normal code does not trigger it.
Common situations: Translating obfuscated apps (proguard/branchy obfuscation), code produced by bytecode-generation frameworks, classes assembled with custom assemblers, or corrupted/class-file-version-mismatched bytecode fed into dex-translator's jar2dex pipeline.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Invalid array type
- not support yet!
- cant find zipfs support
- while accept code in method
- zero-width instruction op=0x%02x
AI-assisted analysis of pxb1988/dex2jar@b5bda4fb49 (2026-09-08).
Data as JSON: /api/errors/655d4207fb7eadd7.
Report an issue: GitHub.
Appendix: source
Thrown at dex-translator/src/main/java/com/googlecode/d2j/converter/J2IRConverter.java:911
if (parentCount[dst] > 1) {
for (int i = 0; i < src.getLocals(); i++) {
JvmValue p = src.getLocal(i);
JvmValue q = distFrame.getLocal(i);
if (p != null) {
if (q == null) {
q = new JvmValue(p.getSize());
distFrame.setLocal(i, q);
}
relate(p, q);
}
}
if (src.getStackSize() > 0) {
if (distFrame.getStackSize() == 0) {
for (int i = 0; i < src.getStackSize(); i++) {
distFrame.push(new JvmValue(src.getStack(i).getSize()));
}
} else if (distFrame.getStackSize() != src.getStackSize()) {
throw new RuntimeException("stack not balanced");
}
for (int i = 0; i < src.getStackSize(); i++) {
JvmValue p = src.getStack(i);
JvmValue q = distFrame.getStack(i);
relate(p, q);
}
}
} else {
distFrame.init(src);
}
}
private void relate(JvmValue parent, JvmValue child) {
if (child.parent == null) {
child.parent = parent;
} else if (child.parent == parent) {
//
} else {View on GitHub (pinned to b5bda4fb49)