skylot/jadx · warning · JadxOverflowException

LoopRegionVisitor.assignOnlyInLoop endless recursion

Error message

LoopRegionVisitor.assignOnlyInLoop endless recursion

What it means

Thrown by LoopRegionVisitor.checkForLoop when the recursive helper assignOnlyInLoop throws a JVM StackOverflowError. jadx detects the SOError, converts it to a JadxOverflowException, and gives up converting that loop into a for/for-each loop. The method is still decompiled; only the loop-shape recognition for that one loop fails.

Source

Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java:133

		InsnNode initInsn = initArg.getAssignInsn();
		if (initInsn == null
				|| initInsn.contains(AFlag.DONT_GENERATE)
				|| initArg.getSVar().getUseCount() != 1) {
			return false;
		}
		if (!usedOnlyInLoop(mth, loopRegion, arg)) {
			return false;
		}
		// can't make loop if argument from increment instruction is assign in loop
		List<RegisterArg> args = new ArrayList<>();
		incrInsn.getRegisterArgs(args);
		for (RegisterArg iArg : args) {
			try {
				if (assignOnlyInLoop(mth, loopRegion, iArg)) {
					return false;
				}
			} catch (StackOverflowError error) {
				throw new JadxOverflowException("LoopRegionVisitor.assignOnlyInLoop endless recursion");
			}
		}

		// all checks passed
		initInsn.add(AFlag.DONT_GENERATE);
		incrInsn.add(AFlag.DONT_GENERATE);

		LoopType arrForEach = checkArrayForEach(mth, loopRegion, initInsn, incrInsn, condition);
		loopRegion.setType(arrForEach != null ? arrForEach : new ForLoop(initInsn, incrInsn));
		return true;
	}

	private static LoopType checkArrayForEach(MethodNode mth, LoopRegion loopRegion, InsnNode initInsn, InsnNode incrInsn,
			IfCondition condition) {
		if (!(incrInsn instanceof ArithNode)) {
			return null;
		}
		ArithNode arithNode = (ArithNode) incrInsn;

View on GitHub (pinned to e738a26571)

Solutions

  1. Upgrade jadx; assignOnlyInLoop has been hardened and de-recursified over releases.
  2. Run the JVM with a larger thread stack (-Xss) if you embed jadx and hit it on borderline inputs.
  3. Ignore it - the method still decompiles, only the for-loop prettification is skipped.
  4. Catch JadxOverflowException around the decompile call if you need the batch to keep going.

Example fix

// before
java -jar jadx-cli.jar app.apk
// after - give the recursive helper more stack
java -Xss4m -jar jadx-cli.jar app.apk
Defensive patterns

Strategy: try-catch

Try / catch

try {
    cls.decompile();
} catch (JadxOverflowException e) {
    if (e.getMessage().contains("assignOnlyInLoop endless recursion")) {
        cls.addError("loop-shape detection overflow", e);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Decompiling a method where the increment expression of a candidate for-loop references registers whose assignment chains are deeply or mutually recursive, so the recursive assignOnlyInLoop walk blows the JVM stack.

Common situations: Obfuscated loop code, loops with complex phi-derived indices, or nested loops where the increment variable feeds back through many SSA edges. The user-visible effect is usually that a loop renders as a while(true) with manual index update instead of a clean for-loop.

Related errors


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