skylot/jadx · error · JadxRuntimeException

Ordering pass not found: {}, listed in 'runAfter' of pass: {

Error message

Ordering pass not found: {}, listed in 'runAfter' of pass: {}
 all passes: {}

What it means

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.

Source

Thrown at jadx-core/src/main/java/jadx/core/utils/PassMerge.java:80

		if (ListUtils.isSingleElement(runBefore, JadxPassInfo.END)) {
			return -1;
		}
		int visitorsCount = visitors.size();
		Map<String, Integer> namePosMap = new HashMap<>(visitorsCount);
		for (int i = 0; i < visitorsCount; i++) {
			namePosMap.put(namesMap.get(visitors.get(i)), i);
		}
		int after = -1;
		for (String name : runAfter) {
			Integer pos = namePosMap.get(name);
			if (pos != null) {
				after = Math.max(after, pos);
			} else {
				if (mergePassesNames.contains(name)) {
					// ignore known passes
					continue;
				}
				throw new JadxRuntimeException("Ordering pass not found: " + name
						+ ", listed in 'runAfter' of pass: " + pass
						+ "\n all passes: " + ListUtils.map(visitors, namesMap::get));
			}
		}
		int before = Integer.MAX_VALUE;
		for (String name : runBefore) {
			Integer pos = namePosMap.get(name);
			if (pos != null) {
				before = Math.min(before, pos);
			} else {
				if (mergePassesNames.contains(name)) {
					// ignore known passes
					continue;
				}
				throw new JadxRuntimeException("Ordering pass not found: " + name
						+ ", listed in 'runBefore' of pass: " + pass
						+ "\n all passes: " + ListUtils.map(visitors, namesMap::get));
			}

View on GitHub (pinned to e738a26571)

Solutions

  1. Read the 'all passes:' list in the message and correct the runAfter() name in your JadxPassInfo to exactly match a built-in pass name.
  2. Verify your plugin's jadx-api dependency version matches the running jadx build (a renamed/removed pass indicates a version skew).
  3. 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.
  4. Drop the runAfter() constraint for that name if you only need loose ordering, or use JadxPassInfo.START to run first.

Example fix

// before
public JadxPassInfo getInfo() {
    return new JadxPassInfo("my-pass", "my pass")
        .runAfter("ModVisitor"); // typo / removed name -> error
}
// after
public JadxPassInfo getInfo() {
    return new JadxPassInfo("my-pass", "my pass")
        .runAfter("MethodInvokeVisitor"); // exact built-in name
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate runAfter names against the live visitor pass list before registering.
Set<String> known = visitors.stream().map(IDexTreeVisitor::getName).collect(Collectors.toSet());
for (String n : passInfo.runAfter()) {
    if (!known.contains(n) && !mergePassNames.contains(n)
        && !JadxPassInfo.START.equals(n) && !JadxPassInfo.END.equals(n)) {
        throw new IllegalArgumentException("runAfter references unknown pass: " + n
            + ". Known: " + known);
    }
}

Try / catch

// Wrap pass registration so a bad ordering hint fails fast with context.
try {
    passMerge.merge(customPasses, wrapFn);
} catch (JadxRuntimeException e) {
    LOG.error("Pass merge failed (check runAfter names/version): {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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