{"record":{"id":"7c798af046e16c52","repo":"facebook/flow","slug":"cannot-insert-array-into-non-array-parent-type","errorCode":null,"errorMessage":"Cannot insert array into non-array parent type: ${parent.type}","messagePattern":"Cannot insert array into non-array parent type: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/flow-parser/oxidized-src/transform/astNodeMutationHelpers.js","lineNumber":105,"sourceCode":"  const replacementParent = getParentKey(\n    originalNode,\n    originalNodeParent,\n    visitorKeys,\n  );\n  const parent = replacementParent.node;\n  if (replacementParent.type === 'array') {\n    // $FlowExpectedError[prop-missing]\n    parent[replacementParent.key] = replaceInArray(\n      // $FlowExpectedError[prop-missing]\n      parent[replacementParent.key],\n      replacementParent.targetIndex,\n      Array.isArray(nodeToReplaceWith)\n        ? nodeToReplaceWith\n        : [nodeToReplaceWith],\n    );\n  } else {\n    if (Array.isArray(nodeToReplaceWith)) {\n      throw new Error(\n        `Cannot insert array into non-array parent type: ${parent.type}`,\n      );\n    }\n    // $FlowExpectedError[prop-missing]\n    parent[replacementParent.key] = nodeToReplaceWith;\n  }\n}\n\n/**\n * Remove a node from the AST its connected to (via the parent pointer).\n */\nexport function removeNodeOnParent(\n  originalNode: ESNode,\n  originalNodeParent: ESNode,\n  visitorKeys?: ?VisitorKeysType,\n): void {\n  const replacementParent = getParentKey(\n    originalNode,","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/facebook/flow/blob/d1341dac899a79c027762f6b423d896045287620/packages/flow-parser/oxidized-src/transform/astNodeMutationHelpers.js#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Return a single wrapping node instead, classically a BlockStatement for statement positions","Only return arrays for nodes you know sit inside an array key such as Program.body or BlockStatement.body","Check the node's parent key before choosing single vs array replacement"],"exampleFix":"// before\nif (x) return [assignStmt, returnStmt]; // array for consequent -> throws\n\n// after\nif (x) {\n  return {type: 'BlockStatement', body: [assignStmt, returnStmt]};\n}","handlingStrategy":"type-guard","validationCode":"const SINGLE_KEYS = new Set(['consequent', 'alternate', 'test', 'init', 'argument', 'discriminant']);\nfunction canReturnArray(parent, key) {\n  return Array.isArray(parent[key]); // only array slots accept array results\n}","typeGuard":"// array replacement is safe only if the slot currently holds an array\nconst slotIsArray = (parent, key) => Array.isArray(parent[key]);\n// visitor: return Array.isArray(result) && !slotIsArray(parent, key)\n//   ? {type: 'BlockStatement', body: result}\n//   : result;","tryCatchPattern":"try {\n  SimpleTransform.transform(ast, options);\n} catch (e) {\n  if (e.message.includes('Cannot insert array into non-array parent')) {\n    throw new Error('Wrap multi-node results in a BlockStatement for single-node slots');\n  }\n  throw e;\n}","preventionTips":["Check Array.isArray(parent[key]) before returning an array from a visitor","Default to wrapping multi-statement results in a BlockStatement for statement slots","Save array returns for slots you know are arrays (body, params, arguments, elements, properties)"],"tags":["ast","replacement","type-mismatch","flow-parser"],"backgroundTag":"ast-replacement-type-mismatch","analyzedSha":"d1341dac899a79c027762f6b423d896045287620","analyzedAt":"2026-08-17T00:07:02.212Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}