{"record":{"id":"ccb3178445ab16d1","repo":"facebook/flow","slug":"tried-to-remove-node-type-from-parent-of-type-ccb317","errorCode":null,"errorMessage":"Tried to remove ${node.type} from parent of type ${node.parent.type}.\nHowever ${node.type} can only be safely removed from parent of type ArrayExpression | ObjectExpression | CallExpression | OptionalCallExpression | NewExpression.","messagePattern":"Tried to remove (.+?) from parent of type (.+?)\\.\nHowever (.+?) can only be safely removed from parent of type ArrayExpression \\| ObjectExpression \\| CallExpression \\| OptionalCallExpression \\| NewExpression\\.","errorType":"exception","errorClass":"InvalidRemovalError","httpStatus":null,"severity":"error","filePath":"packages/flow-transform/src/transform/mutations/RemoveNode.js","lineNumber":258,"sourceCode":"\n      // SpreadElement can be the child of a number of usecases\n      case 'SpreadElement':\n        switch (node.parent.type) {\n          case 'ArrayExpression':\n            return 'elements';\n\n          case 'ObjectExpression':\n            return 'properties';\n\n          // $FlowFixMe[incompatible-type]\n          // $FlowFixMe[invalid-compare]\n          case 'OptionalCallExpression':\n          case 'CallExpression':\n          case 'NewExpression':\n            return 'arguments';\n\n          default:\n            throw new InvalidRemovalError(\n              getErrorMessage([\n                'ArrayExpression',\n                'ObjectExpression',\n                'CallExpression',\n                'OptionalCallExpression',\n                'NewExpression',\n              ]),\n            );\n        }\n\n      default:\n        throw new InvalidRemovalError(\n          `Cannot perform a remove mutation on node of type ${node.type}`,\n        );\n    }\n  })();\n\n  const targetIndex = (() => {","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/facebook/flow/blob/d1341dac899a79c027762f6b423d896045287620/packages/flow-transform/src/transform/mutations/RemoveNode.js#L240-L276","documentation":"The SpreadElement case of removeNodeMutation supports removal from ArrayExpression elements, ObjectExpression properties, and call, new, and optional-call arguments. Any other parent (e.g. a JSXSpreadAttribute) hits the default and throws InvalidRemovalError with the allowlist. Each allowed slot is an array key the engine can splice; other positions are rejected because removing the spread there changes program shape unsafely.","triggerScenarios":"removeNodeMutation(spreadElement) where the parent is a JSXSpreadAttribute, or any type not in the allowlist. Note a spread inside an ArrayPattern is a RestElement, not a SpreadElement, so pattern cleanup should not match these nodes.","commonSituations":"Codemods stripping spread props from JSX (like <Foo {...props} />); generic 'remove all spreads' visitors that do not distinguish SpreadElement from RestElement and JSXSpreadAttribute.","solutions":["Guard on node.parent.type before requesting removal and only allow ArrayExpression, ObjectExpression, CallExpression, OptionalCallExpression, and NewExpression","For JSX spreads, operate on the JSXSpreadAttribute (or replace the whole opening element) instead of the SpreadElement","Use replaceNodeMutation on the enclosing node for unsupported positions"],"exampleFix":"// before\nif (node.type === 'SpreadElement') mutations.push(removeNode(node)); // JSX parent throws\n\n// after\nconst OK = new Set(['ArrayExpression','ObjectExpression','CallExpression','OptionalCallExpression','NewExpression']);\nif (node.type === 'SpreadElement' && OK.has(node.parent.type)) {\n  mutations.push(removeNode(node));\n}","handlingStrategy":"type-guard","validationCode":"const SPREAD_REMOVE_PARENTS = new Set(['ArrayExpression', 'ObjectExpression', 'CallExpression', 'OptionalCallExpression', 'NewExpression']);\nfunction canRemoveSpreadElement(node) {\n  return node.type === 'SpreadElement' && SPREAD_REMOVE_PARENTS.has(node.parent.type);\n}","typeGuard":"const isRemovableSpreadElement = (node) =>\n  node.type === 'SpreadElement' && ['ArrayExpression', 'ObjectExpression', 'CallExpression', 'OptionalCallExpression', 'NewExpression'].includes(node.parent && node.parent.type);","tryCatchPattern":null,"preventionTips":["Distinguish SpreadElement from RestElement and JSXSpreadAttribute before matching","Guard on parent type; JSX spreads need replacement of the opening element, not removal","Encode the allowlist from the error message as a Set and reuse it in the visitor"],"tags":["flow-transform","removal","spread-element","parent-type"],"backgroundTag":"unsupported-ast-node-removal","analyzedSha":"d1341dac899a79c027762f6b423d896045287620","analyzedAt":"2026-08-17T00:07:02.212Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}