skylot/jadx · error · JadxRuntimeException

Conflict order requirements for pass: {} run after: {} run

Error message

Conflict order requirements for pass: {}
 run after: {}
 run before: {}
 passes: {}

What it means

Thrown by PassMerge when a pass's combined runAfter and runBefore constraints are unsatisfiable: the latest 'after' position is >= the earliest 'before' position (before <= after). The pass would have to run both after something that sits at or past where it must run before. The message dumps the pass, its runAfter/runBefore lists, and the full ordered pass list so you can see the conflict.

Source

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

			}
		}
		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;
		return pos >= visitorsCount ? -1 : pos;
	}

	private static final class MergePass {
		private final JadxPass pass;
		private final IDexTreeVisitor visitor;

View on GitHub (pinned to e738a26571)

Solutions

  1. Inspect the 'passes:' order in the message and relax whichever constraint (runAfter or runBefore) is wrong for your pass's intent.
  2. Keep only one directional constraint if you do not need to pin both sides.
  3. Re-resolve names against the current jadx build so positions reflect reality.
  4. If multiple custom passes conflict, ensure their runAfter/runBefore form a DAG (no cycle) and re-run.

Example fix

// before - runAfter late pass, runBefore early pass => before <= after
new JadxPassInfo("p", "p").runAfter("RenameVisitor").runBefore("EarlyVisitor");
// after - keep a single satisfiable constraint
new JadxPassInfo("p", "p").runAfter("RenameVisitor");
Defensive patterns

Strategy: validation

Validate before calling

// After resolving positions, verify runAfter/runBefore are jointly satisfiable.
int after = maxResolvedPos(runAfter);   // -1 if none
int before = minResolvedPos(runBefore); // MAX_VALUE if none
if (after >= 0 && before != Integer.MAX_VALUE && before <= after) {
    throw new IllegalArgumentException(
        "Unsatisfiable ordering: runAfter pos=" + after + " >= runBefore pos=" + before);
}

Try / catch

try {
    passMerge.merge(customPasses, wrapFn);
} catch (JadxRuntimeException e) {
    if (e.getMessage().contains("Conflict order requirements")) {
        LOG.warn("Conflicting pass order, dropping custom pass: {}", e.getMessage());
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: A JadxPass declares runAfter(A) and runBefore(B) but A is positioned at or after B in the visitor order (e.g. runAfter a late pass while runBefore an early one), so no legal insertion index exists.

Common situations: Conflicting ordering hints added independently (one pass wants to run after the rename pass but before an early pass that precedes rename); version skew changing relative positions of built-in passes; a transitive ordering cycle between multiple custom passes.

Related errors


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