facebook/flow · error · Error

Cannot insert array into non-array parent type: ${parent.typ

Error message

Cannot insert array into non-array parent type: ${parent.type}

What it means

When replaceNodeOnParent resolves where the original node lives, a 'single' result means the node is stored as a lone value under a parent key (e.g. IfStatement.consequent, VariableDeclarator.init), not inside an array. Such a slot can hold exactly one node, so when the replacement value is an array of nodes there is nowhere to put the extra nodes and this error is thrown rather than silently dropping them.

Source

Thrown at packages/flow-parser/oxidized-src/transform/astNodeMutationHelpers.js:105

  const replacementParent = getParentKey(
    originalNode,
    originalNodeParent,
    visitorKeys,
  );
  const parent = replacementParent.node;
  if (replacementParent.type === 'array') {
    // $FlowExpectedError[prop-missing]
    parent[replacementParent.key] = replaceInArray(
      // $FlowExpectedError[prop-missing]
      parent[replacementParent.key],
      replacementParent.targetIndex,
      Array.isArray(nodeToReplaceWith)
        ? nodeToReplaceWith
        : [nodeToReplaceWith],
    );
  } else {
    if (Array.isArray(nodeToReplaceWith)) {
      throw new Error(
        `Cannot insert array into non-array parent type: ${parent.type}`,
      );
    }
    // $FlowExpectedError[prop-missing]
    parent[replacementParent.key] = nodeToReplaceWith;
  }
}

/**
 * Remove a node from the AST its connected to (via the parent pointer).
 */
export function removeNodeOnParent(
  originalNode: ESNode,
  originalNodeParent: ESNode,
  visitorKeys?: ?VisitorKeysType,
): void {
  const replacementParent = getParentKey(
    originalNode,

View on GitHub (pinned to d1341dac89)

Solutions

  1. Return a single wrapping node instead, classically a BlockStatement for statement positions
  2. Only return arrays for nodes you know sit inside an array key such as Program.body or BlockStatement.body
  3. Check the node's parent key before choosing single vs array replacement

Example fix

// before
if (x) return [assignStmt, returnStmt]; // array for consequent -> throws

// after
if (x) {
  return {type: 'BlockStatement', body: [assignStmt, returnStmt]};
}
Defensive patterns

Strategy: type-guard

Validate before calling

const SINGLE_KEYS = new Set(['consequent', 'alternate', 'test', 'init', 'argument', 'discriminant']);
function canReturnArray(parent, key) {
  return Array.isArray(parent[key]); // only array slots accept array results
}

Type guard

// array replacement is safe only if the slot currently holds an array
const slotIsArray = (parent, key) => Array.isArray(parent[key]);
// visitor: return Array.isArray(result) && !slotIsArray(parent, key)
//   ? {type: 'BlockStatement', body: result}
//   : result;

Try / catch

try {
  SimpleTransform.transform(ast, options);
} catch (e) {
  if (e.message.includes('Cannot insert array into non-array parent')) {
    throw new Error('Wrap multi-node results in a BlockStatement for single-node slots');
  }
  throw e;
}

Prevention

When it happens

Trigger: A visitor returns an array of nodes (a 'replace with many' result) for a node that occupies a singular position: the non-block consequent or alternate of an if, a declarator's init, the body statement of a block-less loop, a return argument.

Common situations: Codemods that expand one statement into several (e.g. injecting assignments before an expression) applied to if-branches written without braces; generic 'expand' visitors that always return arrays.

Related errors


AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17). Data as JSON: /api/errors/7c798af046e16c52. Report an issue: GitHub.