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

  1. Upgrade jadx and re-test; phi-cleanup convergence bugs are fixed often.
  2. Report the failing input/method so the cleanup can be made monotone.
  3. Exclude the offending method and continue the batch.
  4. 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

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


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