facebook/flow · error

SimpleTransform: invalid array result for root node

Error message

SimpleTransform: invalid array result for root node

What it means

SimpleTransform walks the AST and, when it reaches the rootNode with no parent, must assign exactly one node as the transformed root. If the visitor's result for the root node is an array (multiple replacement nodes), there is nowhere to splice them — a Program root cannot be replaced by several nodes — so it throws this invariant error.

Solutions

  1. Special-case the root in your visitor: if (node.type === 'Program') return programWithNewBody(node, nodes) — a single Program node containing the changes.
  2. Return a single replacement node for the root; never an array.
  3. Do multi-node insertion into an array slot (e.g. node.body of the Program) instead of replacing the root.

Example fix

// before
transform(node) {
  if (node.type === 'Program') return node.body.map(f); // array for root -> throws
}

// after
transform(node) {
  if (node.type === 'Program') return SimpleTransform.nodeWith(node, {body: node.body.map(f)});
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard the root in your visitor before running the transform
function rootSafeTransform(program, transform) {
  return SimpleTransform.transformProgram(program, {
    transform(node) {
      const result = transform(node);
      if (node.type === 'Program' && Array.isArray(result)) {
        throw new TypeError('visitor returned multiple nodes for Program root');
      }
      return result;
    },
  });
}

Type guard

function isSingleNode(v) {
  return v != null && typeof v === 'object' && typeof v.type === 'string' && !Array.isArray(v);
}

Prevention

When it happens

Trigger: Calling SimpleTransform.transform(program, {transform}) where the transform callback returns an array for the Program node itself (or for whatever node you passed as root). Returning arrays is legal only for nodes whose parent slot is an array (e.g. statements inside body).

Common situations: Custom transforms designed to expand one statement into several that accidentally also fire on the root; copy-pasted visitors from array-mutation helpers; passing a non-Program root and returning multiple nodes for it.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/ab266343c4f0f71c. Report an issue: GitHub.

Appendix: source

Thrown at packages/flow-parser/oxidized-src/transform/SimpleTransform.js:103

            setParentPointer(resultNode, parent);

            if (Array.isArray(resultNode)) {
              traversedResultNode = resultNode
                .map(item => this.transform(item, options))
                .filter(item => item != null);
            } else {
              traversedResultNode = this.transform(resultNode, options);
            }
          }

          if (parent == null) {
            if (node !== rootNode) {
              throw new Error(
                'SimpleTransform infra error: Parent not set on non root node, this should not be possible',
              );
            }
            if (Array.isArray(traversedResultNode)) {
              throw new Error(
                'SimpleTransform: invalid array result for root node',
              );
            } else {
              resultRootNode = traversedResultNode;
            }
          } else if (traversedResultNode == null) {
            removeNodeOnParent(node, parent, options.visitorKeys);
          } else {
            replaceNodeOnParent(
              node,
              parent,
              traversedResultNode,
              options.visitorKeys,
            );
            setParentPointer(traversedResultNode, parent);
          }

          throw SimpleTraverser.Skip;

View on GitHub (pinned to f88ac94bcf)