{"record":{"id":"0f7b004111f6815d","repo":"Tencent/matrix","slug":"expected-node-to-have-an-exclusion","errorCode":null,"errorMessage":"Expected node to have an exclusion ","messagePattern":"Expected node to have an exclusion ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-analyzer/src/main/java/com/tencent/matrix/resource/analyzer/utils/ShortestPathFinder.java","lineNumber":259,"sourceCode":"\n        canIgnoreStrings = true;\n        for (Instance targetReference : targetReferences) {\n            if (isString(targetReference)) {\n                canIgnoreStrings = false;\n                break;\n            }\n        }\n\n        final Set<Instance> targetRefSet = new HashSet<>(targetReferences);\n\n        while (!toVisitQueue.isEmpty() || !toVisitIfNoPathQueue.isEmpty()) {\n            ReferenceNode node;\n            if (!toVisitQueue.isEmpty()) {\n                node = toVisitQueue.poll();\n            } else {\n                node = toVisitIfNoPathQueue.poll();\n                if (node.exclusion == null) {\n                    throw new IllegalStateException(\"Expected node to have an exclusion \" + node);\n                }\n            }\n\n            // Termination\n            if (targetRefSet.contains(node.instance)) {\n                results.put(node.instance, new Result(node, node.exclusion != null));\n                targetRefSet.remove(node.instance);\n                if (targetRefSet.isEmpty()) {\n                    break;\n                }\n            }\n\n            if (checkSeen(node)) {\n                continue;\n            }\n\n            if (node.instance instanceof RootObj) {\n                visitRootObj(node);","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-analyzer/src/main/java/com/tencent/matrix/resource/analyzer/utils/ShortestPathFinder.java#L241-L277","documentation":"ShortestPathFinder.findPath processes nodes from toVisitQueue, or from toVisitIfNoPathQueue when the main queue is empty. Nodes in the fallback queue must carry an exclusion (they were enqueued as last-resort paths); if one has a null exclusion, an internal invariant of the path finder is broken and it throws IllegalStateException.","triggerScenarios":"findPath dequeues from toVisitIfNoPathQueue and finds node.exclusion == null — a node was enqueued into the no-path queue without an associated exclusion record, typically due to stale state after a previous search or an enqueue-logic bug.","commonSituations":"Reusing a ShortestPathFinder instance across multiple findPath calls without clearState; heap dumps with unusual reference graphs; Matrix/Haha parser versions with known graph-enqueue bugs.","solutions":["Create a fresh ShortestPathFinder for each findPath call (ensure clearState runs between searches).","Update Matrix to the latest version where ShortestPathFinder invariants were fixed.","Re-capture the hprof; a malformed dump can produce inconsistent reference nodes.","Catch IllegalStateException around the analysis and report analysis failure rather than crashing the caller."],"exampleFix":"// before\nnode = toVisitIfNoPathQueue.poll();\nif (node.exclusion == null) {\n    throw new IllegalStateException(\"Expected node to have an exclusion \" + node);\n}\n// after\nnode = toVisitIfNoPathQueue.poll();\nif (node == null) {\n    break; // search exhausted\n}\nif (node.exclusion == null) {\n    continue; // treat as normal node instead of failing the whole search\n}","handlingStrategy":"try-catch","validationCode":"// run one findPath per freshly constructed ShortestPathFinder instance","typeGuard":null,"tryCatchPattern":"try { return finder.findPath(startObjects, targetRefs, excludeRefs); } catch (IllegalStateException e) { return analysisFailed(e); }","preventionTips":["Never reuse a ShortestPathFinder across searches","Keep Matrix updated for path-finder fixes","Catch and report analysis failures instead of crashing"],"tags":["heap-dump","graph-search","internal-state","android"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}