skylot/jadx · error · JadxRuntimeException
Phi nodes fix limit reached!
Error message
Phi nodes fix limit reached!
What it means
Thrown by SSATransform.tryToFixUselessPhi when the useless-phi cleanup loop keeps reporting changes (fixUselessPhi returns true) beyond 2 * sVars.size() attempts. The cleanup removes phi nodes whose result is unused and merges phis with identical args; it must reach a fixed point, so exceeding the cap means the cleanup is oscillating or removing-and-recreating phis.
Source
Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java:264
RegisterArg arg = phi.getArg(i);
InsnNode parentInsn = arg.getAssignInsn();
if (parentInsn != null && parentInsn.contains(AFlag.REMOVE)) {
phi.removeArg(arg);
InsnRemover.remove(mth, block, parentInsn);
removed = true;
}
}
}
}
return removed;
}
private static void tryToFixUselessPhi(MethodNode mth) {
int k = 0;
int maxTries = mth.getSVars().size() * 2;
while (fixUselessPhi(mth)) {
if (k++ > maxTries) {
throw new JadxRuntimeException("Phi nodes fix limit reached!");
}
}
}
private static boolean fixUselessPhi(MethodNode mth) {
boolean changed = false;
List<PhiInsn> insnToRemove = new ArrayList<>();
for (SSAVar var : mth.getSVars()) {
// phi result not used
if (var.getUseCount() == 0) {
InsnNode assignInsn = var.getAssign().getParentInsn();
if (assignInsn != null && assignInsn.getType() == InsnType.PHI) {
insnToRemove.add((PhiInsn) assignInsn);
changed = true;
}
}
}
for (BlockNode block : mth.getBasicBlocks()) {View on GitHub (pinned to e738a26571)
Solutions
- Upgrade jadx and re-test; phi-cleanup convergence bugs are fixed often.
- Report the failing input/method so the cleanup can be made monotone.
- Exclude the offending method and continue the batch.
- Wrap per-method decompilation in try/catch when using jadx programmatically.
Defensive patterns
Strategy: try-catch
Try / catch
try {
cls.decompile();
} catch (JadxRuntimeException e) {
if (e.getMessage().contains("Phi nodes fix limit reached")) {
cls.addError("phi cleanup non-convergence", e);
} else { throw e; }
} Prevention
- Report the method - phi cleanup should always converge.
- Update jadx; SSA pass changes are frequent.
- Catch per method to keep batch runs resilient.
When it happens
Trigger: A method whose SSA form, after placement, has phi nodes that fixUselessPhi cannot stabilize: each round removes some phi nodes which then makes others qualify for removal, but the cycle does not converge within 2 * |SSA vars| rounds. Typical with complex try/catch interactions or many merge points.
Common situations: Observed on methods with dense exception handling, switch-try-catch combinations (cf. TestSwitchWithTryCatch in comments near this code), or obfuscated inputs that produce unusually large phi clusters. Like 183, this is essentially an internal jadx invariant violation.
Related errors
- Several immutable types in one variable: {}, vars: {}
- Duplicate predecessors in PHI insn: {}, {}
- Block not found by insn: {}
- Try blocks wrapping queue limit reached! Please report as an
- Same handlers in try block: {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/5d7d68b0cfefadba.
Report an issue: GitHub.