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

  1. Update jadx — JSR resolution has historical edge cases that get patched periodically
  2. Report the class file as a jadx issue — JSR resolution failures are rare and worth investigating
  3. If the input is a .class file, try recompiling or converting with a modern compiler first
  4. 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

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


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