skylot/jadx · critical · JadxRuntimeException
A traverser handler which was not expected to change path st
Error message
A traverser handler which was not expected to change path states actually did
What it means
Thrown in the TraverserController when processing a single traverser state. A handler in the NOT_READY or AWAITING_OPTIONAL_PREDECESSOR_MERGE comparison state is expected to process in-place — returning exactly one result that is the same active path state object. If the handler returns a different result count or a different state object, the in-place processing invariant is violated.
Source
Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/TraverserController.java:183
return List.of(state);
}
/**
* Advances a singular state once.
*
* @return Whether this state has been aborted by the state abort function.
*/
private boolean advanceSingleState(TraverserActivePathState activePathState, TraverserState singleState,
boolean hasReadyToCompare) throws TraverserException {
boolean stateAborted = stateAbortCondition != null && stateAbortCondition.apply(singleState);
if (stateAbortCondition == null || !stateAborted) {
if (singleState.getCompareState() == TraverserState.ComparisonState.NOT_READY
|| singleState.getCompareState() == TraverserState.ComparisonState.AWAITING_OPTIONAL_PREDECESSOR_MERGE
&& hasReadyToCompare) {
AbstractBlockTraverserHandler handler = singleState.getNextHandler();
List<TraverserActivePathState> results = processHandlerImplementations(activePathState, handler);
if (results.size() != 1 || results.get(0) != activePathState) {
throw new JadxRuntimeException("A traverser handler which was not expected to change path states actually did");
}
}
}
return stateAborted;
}
}
View on GitHub (pinned to e738a26571)
Solutions
- Update jadx — traverser state machine bugs are patched promptly
- Report as a jadx issue with the full stack trace and APK if available
- This is an internal contract violation; not fixable from user input
Defensive patterns
Strategy: try-catch
Try / catch
try {
jadxDecompiler.load();
jadxDecompiler.save();
} catch (JadxRuntimeException e) {
if (e.getMessage().contains("not expected to change path states")) {
LOG.error("Internal jadx traverser state machine bug — update jadx", e);
for (ClassNode cls : jadxDecompiler.getClasses()) {
try {
jadxDecompiler.decompileClass(cls);
} catch (JadxRuntimeException ex) {
LOG.warn("Skipped: {}", cls.getFullName());
}
}
} else {
throw e;
}
} Prevention
- Update jadx — this is an internal contract violation in the traverser state machine
- Report as a jadx issue with the full stack trace
- Decompile classes individually to isolate and skip the problematic class
- Use an alternative decompiler as fallback for the affected class
When it happens
Trigger: processHandlerImplementations returns a list with size != 1 or where results.get(0) is not the same object reference as activePathState. The handler split, merged, or replaced the path state when it was contractually required to modify in-place.
Common situations: Internal inconsistency in the traverser state machine — a handler incorrectly modifies path states when it should only update internal state; typically a jadx development bug in the finally-block traversal logic, not input-driven.
Related errors
- A sealed class, AbstractBlockPathTraverserHandler, has an un
- A state of class {} has duplicated to produce a class of {}
- Unknown var type for: {}
- Invalid args count in insn: {}
- Unexpected InsnArg types: {} and {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/080260b570d21162.
Report an issue: GitHub.