{"record":{"id":"c143f55d888c01cf","repo":"skylot/jadx","slug":"unknown-global-traverser-state-has-a-global-state","errorCode":null,"errorMessage":"Unknown global traverser state. Has a global state been duplicated?","messagePattern":"Unknown global traverser state\\. Has a global state been duplicated\\?","errorType":"exception","errorClass":"JadxRuntimeException","httpStatus":null,"severity":"error","filePath":"jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/MergePathActivePathTraverserHandler.java","lineNumber":61,"sourceCode":"\t\t}\n\t\treturn blockInfo.getBlock() == terminus;\n\t}\n\n\tprivate static Function<TraverserState, Boolean> getStateAbortOnTerminusFunction(\n\t\t\tIdentifiedScopeWithTerminatorTraverserState finallyState,\n\t\t\tIdentifiedScopeWithTerminatorTraverserState candidateState) {\n\t\tBlockNode finallyTerminus = finallyState.getTerminus();\n\t\tBlockNode candidateTerminus = candidateState.getTerminus();\n\t\tGlobalTraverserSourceState finallyGlobalState = finallyState.getGlobalState();\n\t\tGlobalTraverserSourceState candidateGlobalState = candidateState.getGlobalState();\n\n\t\treturn (final TraverserState state) -> {\n\t\t\tif (state.getGlobalState() == finallyGlobalState) {\n\t\t\t\treturn isStateOnTerminus(state, finallyTerminus);\n\t\t\t} else if (state.getGlobalState() == candidateGlobalState) {\n\t\t\t\treturn isStateOnTerminus(state, candidateTerminus);\n\t\t\t} else {\n\t\t\t\tthrow new JadxRuntimeException(\"Unknown global traverser state. Has a global state been duplicated?\");\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static PostMergeStatus getScopeSplitPostMergeStatus(List<TraverserActivePathState> pathsTaken) {\n\t\t// If the scope split is the same, all branches must not end in a terminator.\n\t\tPostMergeStatus status = new PostMergeStatus();\n\t\tfor (TraverserActivePathState path : pathsTaken) {\n\t\t\tTraverserState finallyState;\n\t\t\tTraverserState candidateState;\n\t\t\tif (path.getFinallyState().isTerminal() || path.getCandidateState().isTerminal()) {\n\t\t\t\tTraverserState rawFinallyState = path.getFinallyState();\n\t\t\t\tTraverserState rawCandidateState = path.getCandidateState();\n\t\t\t\tboolean finallyIsCached = rawFinallyState instanceof RecoveredFromCacheTraverserState;\n\t\t\t\tboolean candidateIsCached = rawCandidateState instanceof RecoveredFromCacheTraverserState;\n\t\t\t\tif (!(finallyIsCached && candidateIsCached)) {\n\t\t\t\t\tstatus.perfectMatch = false;\n\t\t\t\t\tcontinue;","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-core/src/main/java/jadx/core/dex/visitors/finaly/traverser/handlers/MergePathActivePathTraverserHandler.java#L43-L79","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["jadx","decompiler","finally-traverser","merge-path","global-state","internal-invariant"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}