{"record":{"id":"1fa0bdd546e21e03","repo":"rectorphp/rector","slug":"invalid-node-structure-contains-nested-arrays","errorCode":null,"errorMessage":"Invalid node structure: Contains nested arrays","messagePattern":"Invalid node structure: Contains nested arrays","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/PhpParser/NodeTraverser/RectorNodeTraverser.php","lineNumber":196,"sourceCode":"            if ($traverseChildren) {\n                $this->traverseNode($subNode);\n                if ($this->stopTraversal) {\n                    break;\n                }\n            }\n        }\n    }\n    /**\n     * @param Node[] $nodes\n     * @return Node[]\n     */\n    private function traverseArray(array $nodes): array\n    {\n        $doNodes = [];\n        foreach ($nodes as $i => $node) {\n            if (!$node instanceof Node) {\n                if (\\is_array($node)) {\n                    throw new LogicException('Invalid node structure: Contains nested arrays');\n                }\n                continue;\n            }\n            $traverseChildren = \\true;\n            $currentNodeVisitors = $this->getVisitorsForNode($node);\n            foreach ($currentNodeVisitors as $currentNodeVisitor) {\n                $return = $currentNodeVisitor->enterNode($node);\n                if ($return !== null) {\n                    if ($return instanceof Node) {\n                        $originalNodeNodeClass = get_class($node);\n                        $this->ensureReplacementReasonable($node, $return);\n                        $nodes[$i] = $node = $return;\n                        if ($originalNodeNodeClass !== get_class($return)) {\n                            // stop traversing as node type changed and visitors won't work\n                            continue 2;\n                        }\n                    } elseif (\\is_array($return)) {\n                        $doNodes[] = [$i, $return];","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/src/PhpParser/NodeTraverser/RectorNodeTraverser.php#L178-L214","documentation":"RectorNodeTraverser::traverseArray() iterates a node array (statement list, argument list) where every element must be a Node or a non-Node placeholder it can skip. An element that is itself a PHP array has no visitor semantics — the traverser cannot descend into it — so it throws LogicException('Invalid node structure: Contains nested arrays').","triggerScenarios":"A custom NodeVisitor or rule returns a nested array where a flat node list is expected: return [[$stmtA, $stmtB]]; from leaveNode() replacement, or refactor() returning array-of-arrays on one code path.","commonSituations":"Refactoring a rule that groups generated statements per match and forgets array_merge/array_shift; upgrading php-parser versions where replacement arrays became stricter; a rule builder helper that wraps results in an extra layer.","solutions":["Flatten the return value: return [$stmtA, $stmtB]; (one array of Node, not arrays of arrays)","If aggregating, use array_merge(...$groups) before returning","Add a unit test asserting every element of refactor()'s array return is PhpParser\\Node"],"exampleFix":"// before\nreturn [[$newAssign, $newReturn]];\n\n// after\nreturn [$newAssign, $newReturn];","handlingStrategy":"validation","validationCode":"// guard any node-array you hand back from a visitor\nfunction assertFlatNodeArray(array $nodes): array\n{\n    foreach ($nodes as $n) {\n        if (is_array($n)) {\n            throw new LogicException('Nested node array detected; flatten before returning.');\n        }\n    }\n    return $nodes;\n}","typeGuard":"function isFlatNodeArray(array $nodes): bool\n{\n    foreach ($nodes as $n) {\n        if (is_array($n)) {\n            return false;\n        }\n    }\n    return true;\n}","tryCatchPattern":"try {\n    $result = $visitor->enterNode($node);\n} catch (\\LogicException $e) {\n    // rule-internal bug: fail the test run loudly with the visitor class name\n    $this->fail(get_class($visitor) . ': ' . $e->getMessage());\n}","preventionTips":["Unit-test custom rules through AbstractRectorTestCase so nested returns fail before production","When aggregating node groups, always array_merge(...$groups) at the boundary","Annotate return types (@return Node[]) so static analysis flags array-of-arrays"],"tags":["php","rector","php-parser","ast","node-visitor"],"backgroundTag":"invalid-ast-structure","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}