skylot/jadx · warning · JadxRuntimeException
Fail to resolve jsr instructions
Error message
Fail to resolve jsr instructions
What it means
Thrown during JSR/RET subroutine resolution. JSR (Jump SubRoutine) is a deprecated Java bytecode instruction used for finally blocks. Jadx resolves it by duplicating subroutine code for each call site iteratively. If the resolution doesn't converge within blocksCount iterations, the subroutine structure is too complex to unwind.
Source
Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/blocks/ResolveJavaJSR.java:32
import jadx.core.utils.exceptions.JadxRuntimeException;
/**
* Duplicate code to resolve java jsr/ret.
* JSR (jump subroutine) allows executing the same code from different places.
* Used mostly for 'finally' blocks, deprecated in Java 7.
*/
public class ResolveJavaJSR {
public static void process(MethodNode mth) {
int blocksCount = mth.getBasicBlocks().size();
int k = 0;
while (true) {
boolean changed = resolve(mth);
if (!changed) {
break;
}
if (k++ > blocksCount) {
throw new JadxRuntimeException("Fail to resolve jsr instructions");
}
}
}
private static boolean resolve(MethodNode mth) {
List<BlockNode> blocks = mth.getBasicBlocks();
int blocksCount = blocks.size();
for (BlockNode block : blocks) {
if (BlockUtils.checkLastInsnType(block, InsnType.JAVA_RET)) {
resolveForRetBlock(mth, block);
if (blocksCount != mth.getBasicBlocks().size()) {
return true;
}
}
}
return false;
}
View on GitHub (pinned to e738a26571)
Solutions
- Update jadx — JSR resolution has historical edge cases that get patched periodically
- Report the class file as a jadx issue — JSR resolution failures are rare and worth investigating
- If the input is a .class file, try recompiling or converting with a modern compiler first
- Exclude the class if JSR-based finally blocks are not critical to analysis
Defensive patterns
Strategy: try-catch
Validate before calling
// Check if input class files use legacy JSR instructions before decompiling
// JSR/RET are deprecated since Java 6, removed in Java 7+ class file versions
try {
ClassReader cr = new ClassReader(Files.readAllBytes(classFile.toPath()));
if (cr.readByte(6) < 51) { // major version < 51 (Java 7)
LOG.info("Legacy class file (pre-Java 7) may contain JSR instructions");
}
} catch (Exception ignored) {} Try / catch
try {
jadxDecompiler.load();
jadxDecompiler.save();
} catch (JadxRuntimeException e) {
if (e.getMessage().contains("Fail to resolve jsr instructions")) {
LOG.warn("JSR/RET subroutine too complex to resolve, skipping class");
for (ClassNode cls : jadxDecompiler.getClasses()) {
try {
jadxDecompiler.decompileClass(cls);
} catch (JadxRuntimeException ex) {
LOG.warn("Skipped: {}", cls.getFullName());
}
}
} else {
throw e;
}
} Prevention
- For .class file inputs, recompile with a modern compiler (Java 7+) to eliminate JSR
- Convert legacy class files through a modern toolchain before decompiling
- Keep jadx updated — JSR resolution has historical edge cases that get patched
- Exclude classes with known JSR patterns if finally-block analysis is not critical
When it happens
Trigger: The resolve() method returns true (changed) more than blocksCount times, meaning each iteration adds or modifies blocks but never reaches a fixed point. The subroutine pattern creates a non-terminating expansion.
Common situations: Legacy Java bytecode (pre-Java 7) with deeply nested or mutually recursive JSR subroutines; obfuscated code that uses JSR-like patterns synthetically; DEX files converted from old class files that retain JSR instructions.
Related errors
- Try blocks wrapping queue limit reached! Please report as an
- Failed to find top block for try-catch from: {}
- Unreachable block: {}
- Copy blocks tree failed. Missing block for connection: {}
- Failed to generate code for class: ${cls.getFullName()}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/eb8d05528e401a24.
Report an issue: GitHub.