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
- Upgrade jadx; assignOnlyInLoop has been hardened and de-recursified over releases.
- Run the JVM with a larger thread stack (-Xss) if you embed jadx and hit it on borderline inputs.
- Ignore it - the method still decompiles, only the for-loop prettification is skipped.
- 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
- Give the JVM a larger stack (-Xss4m) when embedding jadx against hard inputs.
- Update jadx; assignOnlyInLoop is de-recursified over time.
- This is non-fatal to output quality - only for-loop prettification is skipped.
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
- Failed to generate code for class: ${cls.getFullName()}
- Failed to process class: ${cls.getFullName()}
- Failed to process method to visitor:
- Anonymous inner class unlimited recursion detected. Convert
- Bad name for type variable: {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/4497238291a5c6c8.
Report an issue: GitHub.