skylot/jadx · error · JadxRuntimeException

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

Error message

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

What it means

Mirror of the runAfter case: PassMerge.searchInsertPos() iterates runBefore() and throws when a listed name is not found in the visitor position map and is not a known merge pass. The message names the missing pass, the pass that declared it, and the full pass list. Unlike runAfter, an unresolved runBefore name cannot be satisfied, so the pass cannot be positioned and the merge aborts.

Source

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

					// 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));
			}
		}
		if (before <= after) {
			throw new JadxRuntimeException("Conflict order requirements for pass: " + pass
					+ "\n run after: " + runAfter
					+ "\n run before: " + runBefore
					+ "\n passes: " + ListUtils.map(visitors, namesMap::get));
		}
		if (after == -1) {
			if (before == Integer.MAX_VALUE) {
				// not ordered, put at last
				return -1;
			}
			return before;
		}
		int pos = after + 1;

View on GitHub (pinned to e738a26571)

Solutions

  1. Match the runBefore() name to an entry in the 'all passes:' list shown in the error.
  2. Align your plugin's jadx-api version with the running jadx to avoid referencing removed/renamed passes.
  3. If the target is a custom pass, make sure that pass's plugin is loaded (appears in mergePassesNames) before declaring runBefore.
  4. Use JadxPassInfo.END to run last, or remove the runBefore entry if strict ordering is unnecessary.

Example fix

// before
new JadxPassInfo("my-pass", "my pass").runBefore("ClassModifier"); // wrong name
// after
new JadxPassInfo("my-pass", "my pass").runBefore("ClassModifierVisitor"); // exact name
Defensive patterns

Strategy: validation

Validate before calling

Set<String> known = visitors.stream().map(IDexTreeVisitor::getName).collect(Collectors.toSet());
for (String n : passInfo.runBefore()) {
    if (!known.contains(n) && !mergePassNames.contains(n)
        && !JadxPassInfo.START.equals(n) && !JadxPassInfo.END.equals(n)) {
        throw new IllegalArgumentException("runBefore references unknown pass: " + n);
    }
}

Try / catch

try {
    passMerge.merge(customPasses, wrapFn);
} catch (JadxRuntimeException e) {
    LOG.error("Pass merge failed (check runBefore names/version): {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: A custom JadxPass declares info().runBefore("foo") where "foo" is absent from built-in visitors and not a registered merge pass. Raised in the runBefore loop at PassMerge line 95.

Common situations: Version skew after a jadx upgrade that renamed/removed a pass you tried to run before; typo in runBefore(); depending on a custom pass that another (not-yet-loaded) plugin should have provided.

Related errors


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