skylot/jadx · error · JadxRuntimeException
Unknown global traverser state. Has a global state been dupl
Error message
Unknown global traverser state. Has a global state been duplicated?
What it means
Thrown inside an abort-on-terminus predicate created by MergePathActivePathTraverserHandler. The predicate checks whether a traverser state's global state matches the finally global state or the candidate global state. If it matches neither, the finally/candidate global state partitioning is broken — the error message asks whether a global state was inadvertently duplicated. This protects the invariant that every traverser state belongs to exactly one of two mutually exclusive global state groups during a scope-split merge.
Source
Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/MergePathActivePathTraverserHandler.java:61
}
return blockInfo.getBlock() == terminus;
}
private static Function<TraverserState, Boolean> getStateAbortOnTerminusFunction(
IdentifiedScopeWithTerminatorTraverserState finallyState,
IdentifiedScopeWithTerminatorTraverserState candidateState) {
BlockNode finallyTerminus = finallyState.getTerminus();
BlockNode candidateTerminus = candidateState.getTerminus();
GlobalTraverserSourceState finallyGlobalState = finallyState.getGlobalState();
GlobalTraverserSourceState candidateGlobalState = candidateState.getGlobalState();
return (final TraverserState state) -> {
if (state.getGlobalState() == finallyGlobalState) {
return isStateOnTerminus(state, finallyTerminus);
} else if (state.getGlobalState() == candidateGlobalState) {
return isStateOnTerminus(state, candidateTerminus);
} else {
throw new JadxRuntimeException("Unknown global traverser state. Has a global state been duplicated?");
}
};
}
private static PostMergeStatus getScopeSplitPostMergeStatus(List<TraverserActivePathState> pathsTaken) {
// If the scope split is the same, all branches must not end in a terminator.
PostMergeStatus status = new PostMergeStatus();
for (TraverserActivePathState path : pathsTaken) {
TraverserState finallyState;
TraverserState candidateState;
if (path.getFinallyState().isTerminal() || path.getCandidateState().isTerminal()) {
TraverserState rawFinallyState = path.getFinallyState();
TraverserState rawCandidateState = path.getCandidateState();
boolean finallyIsCached = rawFinallyState instanceof RecoveredFromCacheTraverserState;
boolean candidateIsCached = rawCandidateState instanceof RecoveredFromCacheTraverserState;
if (!(finallyIsCached && candidateIsCached)) {
status.perfectMatch = false;
continue;View on GitHub (pinned to e738a26571)
Solutions
- Report to jadx developers with the input file and full stack trace.
- Workaround: skip the finally visitor pass for this input.
- If developing jadx, verify that GlobalTraverserSourceState instances are not re-created during duplication — the same object references must flow from the original active path into all duplicated states.
Defensive patterns
Strategy: validation
Prevention
- Internal invariant in the merge-path handler; not user-guardable.
- If developing jadx, ensure GlobalTraverserSourceState references are preserved (not re-created) during active path duplication.
- Report inputs that trigger this with a full stack trace.
When it happens
Trigger: During scope-split merge processing, a TraverserController is launched with an abort function. The function is called on states produced during sub-traversal. If a sub-traversal state's getGlobalState() (which delegates to getGlobalStateFor via the comparator) returns a GlobalTraverserSourceState that is reference-unequal to both the finally and candidate global states captured at predicate-creation time, this fires.
Common situations: Complex multi-branch finally blocks where the scope split introduces new global states, or where TraverserActivePathState.duplicate() fails to propagate the original global state references. Triggered by obfuscated bytecode with dense try-finally structures.
Related errors
- Orphaned TraverserState node
- Expected to find block info within {}
- Orphaned traverser state
- Orphaned traverser state
- Attempted to get instructions slice of block {} with {} skip
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/c143f55d888c01cf.
Report an issue: GitHub.