{"record":{"id":"b6cd61affc4bf08d","repo":"skylot/jadx","slug":"ordering-pass-not-found-listed-in-runafter","errorCode":null,"errorMessage":"Ordering pass not found: {}, listed in 'runAfter' of pass: {}\n all passes: {}","messagePattern":"Ordering pass not found: (.+?), listed in 'runAfter' of pass: (.+?)\n all passes: (.+?)","errorType":"exception","errorClass":"JadxRuntimeException","httpStatus":null,"severity":"error","filePath":"jadx-core/src/main/java/jadx/core/utils/PassMerge.java","lineNumber":80,"sourceCode":"\t\tif (ListUtils.isSingleElement(runBefore, JadxPassInfo.END)) {\n\t\t\treturn -1;\n\t\t}\n\t\tint visitorsCount = visitors.size();\n\t\tMap<String, Integer> namePosMap = new HashMap<>(visitorsCount);\n\t\tfor (int i = 0; i < visitorsCount; i++) {\n\t\t\tnamePosMap.put(namesMap.get(visitors.get(i)), i);\n\t\t}\n\t\tint after = -1;\n\t\tfor (String name : runAfter) {\n\t\t\tInteger pos = namePosMap.get(name);\n\t\t\tif (pos != null) {\n\t\t\t\tafter = Math.max(after, pos);\n\t\t\t} else {\n\t\t\t\tif (mergePassesNames.contains(name)) {\n\t\t\t\t\t// ignore known passes\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow new JadxRuntimeException(\"Ordering pass not found: \" + name\n\t\t\t\t\t\t+ \", listed in 'runAfter' of pass: \" + pass\n\t\t\t\t\t\t+ \"\\n all passes: \" + ListUtils.map(visitors, namesMap::get));\n\t\t\t}\n\t\t}\n\t\tint before = Integer.MAX_VALUE;\n\t\tfor (String name : runBefore) {\n\t\t\tInteger pos = namePosMap.get(name);\n\t\t\tif (pos != null) {\n\t\t\t\tbefore = Math.min(before, pos);\n\t\t\t} else {\n\t\t\t\tif (mergePassesNames.contains(name)) {\n\t\t\t\t\t// ignore known passes\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow new JadxRuntimeException(\"Ordering pass not found: \" + name\n\t\t\t\t\t\t+ \", listed in 'runBefore' of pass: \" + pass\n\t\t\t\t\t\t+ \"\\n all passes: \" + ListUtils.map(visitors, namesMap::get));\n\t\t\t}","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-core/src/main/java/jadx/core/utils/PassMerge.java#L62-L98","documentation":"Thrown by jadx's PassMerge when merging a custom JadxPass (from a plugin or registered pass) whose JadxPassInfo.runAfter() names a pass that is neither a built-in visitor nor a known merge pass. The message lists the offending name, the pass that declared it, and the full visitor-name list so you can compare. PassMerge computes an insertion position by looking up every name in runAfter against the current visitor order; an unresolved name is fatal because the ordering cannot be satisfied.","triggerScenarios":"Registering a plugin JadxPass whose info().runAfter(\"foo\") references a pass name that does not exist among built-in IDexTreeVisitor passes and is not itself a registered merge pass. Happens in PassMerge.merge() -> searchInsertPos() at the runAfter loop when namePosMap.get(name)==null and mergePassesNames.contains(name)==false.","commonSituations":"Plugin/jadx version mismatch where a pass was renamed or removed in a newer jadx (e.g. a pass renamed across a major release); a typo in the pass name string in runAfter(); a custom pass that declares a dependency on another custom pass that was never registered because its plugin failed to load.","solutions":["Read the 'all passes:' list in the message and correct the runAfter() name in your JadxPassInfo to exactly match a built-in pass name.","Verify your plugin's jadx-api dependency version matches the running jadx build (a renamed/removed pass indicates a version skew).","If the referenced pass is another custom pass, ensure that pass's plugin is actually loaded before yours (check plugin load logs) so it appears in mergePassesNames.","Drop the runAfter() constraint for that name if you only need loose ordering, or use JadxPassInfo.START to run first."],"exampleFix":"// before\npublic JadxPassInfo getInfo() {\n    return new JadxPassInfo(\"my-pass\", \"my pass\")\n        .runAfter(\"ModVisitor\"); // typo / removed name -> error\n}\n// after\npublic JadxPassInfo getInfo() {\n    return new JadxPassInfo(\"my-pass\", \"my pass\")\n        .runAfter(\"MethodInvokeVisitor\"); // exact built-in name\n}","handlingStrategy":"validation","validationCode":"// Validate runAfter names against the live visitor pass list before registering.\nSet<String> known = visitors.stream().map(IDexTreeVisitor::getName).collect(Collectors.toSet());\nfor (String n : passInfo.runAfter()) {\n    if (!known.contains(n) && !mergePassNames.contains(n)\n        && !JadxPassInfo.START.equals(n) && !JadxPassInfo.END.equals(n)) {\n        throw new IllegalArgumentException(\"runAfter references unknown pass: \" + n\n            + \". Known: \" + known);\n    }\n}","typeGuard":null,"tryCatchPattern":"// Wrap pass registration so a bad ordering hint fails fast with context.\ntry {\n    passMerge.merge(customPasses, wrapFn);\n} catch (JadxRuntimeException e) {\n    LOG.error(\"Pass merge failed (check runAfter names/version): {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Pin your plugin's jadx-api version to the exact jadx build you target.","Copy built-in pass names from the jadx source rather than typing them.","When upgrading jadx, diff the built-in pass list for renames/removals.","Prefer JadxPassInfo.START/END sentinels over naming specific passes when possible."],"tags":["jadx","plugins","pass-ordering","configuration"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}