{"record":{"id":"5d7d68b0cfefadba","repo":"skylot/jadx","slug":"phi-nodes-fix-limit-reached","errorCode":null,"errorMessage":"Phi nodes fix limit reached!","messagePattern":"Phi nodes fix limit reached!","errorType":"exception","errorClass":"JadxRuntimeException","httpStatus":null,"severity":"error","filePath":"jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java","lineNumber":264,"sourceCode":"\t\t\t\t\tRegisterArg arg = phi.getArg(i);\n\t\t\t\t\tInsnNode parentInsn = arg.getAssignInsn();\n\t\t\t\t\tif (parentInsn != null && parentInsn.contains(AFlag.REMOVE)) {\n\t\t\t\t\t\tphi.removeArg(arg);\n\t\t\t\t\t\tInsnRemover.remove(mth, block, parentInsn);\n\t\t\t\t\t\tremoved = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t}\n\n\tprivate static void tryToFixUselessPhi(MethodNode mth) {\n\t\tint k = 0;\n\t\tint maxTries = mth.getSVars().size() * 2;\n\t\twhile (fixUselessPhi(mth)) {\n\t\t\tif (k++ > maxTries) {\n\t\t\t\tthrow new JadxRuntimeException(\"Phi nodes fix limit reached!\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean fixUselessPhi(MethodNode mth) {\n\t\tboolean changed = false;\n\t\tList<PhiInsn> insnToRemove = new ArrayList<>();\n\t\tfor (SSAVar var : mth.getSVars()) {\n\t\t\t// phi result not used\n\t\t\tif (var.getUseCount() == 0) {\n\t\t\t\tInsnNode assignInsn = var.getAssign().getParentInsn();\n\t\t\t\tif (assignInsn != null && assignInsn.getType() == InsnType.PHI) {\n\t\t\t\t\tinsnToRemove.add((PhiInsn) assignInsn);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (BlockNode block : mth.getBasicBlocks()) {","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java#L246-L282","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    cls.decompile();\n} catch (JadxRuntimeException e) {\n    if (e.getMessage().contains(\"Phi nodes fix limit reached\")) {\n        cls.addError(\"phi cleanup non-convergence\", e);\n    } else { throw e; }\n}","preventionTips":["Report the method - phi cleanup should always converge.","Update jadx; SSA pass changes are frequent.","Catch per method to keep batch runs resilient."],"tags":["decompiler","ssa","phi","convergence","exception-handling"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}