facebook/flow · error · InvalidRemovalError
Tried to remove ${node.type} from parent of type ${node.pare
Error message
Tried to remove ${node.type} from parent of type ${node.parent.type}.
However ${node.type} can only be safely removed from parent of type ArrayExpression | ObjectExpression | CallExpression | OptionalCallExpression | NewExpression. What it means
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.
Source
Thrown at packages/flow-transform/src/transform/mutations/RemoveNode.js:258
// SpreadElement can be the child of a number of usecases
case 'SpreadElement':
switch (node.parent.type) {
case 'ArrayExpression':
return 'elements';
case 'ObjectExpression':
return 'properties';
// $FlowFixMe[incompatible-type]
// $FlowFixMe[invalid-compare]
case 'OptionalCallExpression':
case 'CallExpression':
case 'NewExpression':
return 'arguments';
default:
throw new InvalidRemovalError(
getErrorMessage([
'ArrayExpression',
'ObjectExpression',
'CallExpression',
'OptionalCallExpression',
'NewExpression',
]),
);
}
default:
throw new InvalidRemovalError(
`Cannot perform a remove mutation on node of type ${node.type}`,
);
}
})();
const targetIndex = (() => {View on GitHub (pinned to d1341dac89)
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
Example fix
// before
if (node.type === 'SpreadElement') mutations.push(removeNode(node)); // JSX parent throws
// after
const OK = new Set(['ArrayExpression','ObjectExpression','CallExpression','OptionalCallExpression','NewExpression']);
if (node.type === 'SpreadElement' && OK.has(node.parent.type)) {
mutations.push(removeNode(node));
} Defensive patterns
Strategy: type-guard
Validate before calling
const SPREAD_REMOVE_PARENTS = new Set(['ArrayExpression', 'ObjectExpression', 'CallExpression', 'OptionalCallExpression', 'NewExpression']);
function canRemoveSpreadElement(node) {
return node.type === 'SpreadElement' && SPREAD_REMOVE_PARENTS.has(node.parent.type);
} Type guard
const isRemovableSpreadElement = (node) => node.type === 'SpreadElement' && ['ArrayExpression', 'ObjectExpression', 'CallExpression', 'OptionalCallExpression', 'NewExpression'].includes(node.parent && node.parent.type);
Prevention
- 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
When it happens
Trigger: 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.
Common situations: Codemods stripping spread props from JSX (like <Foo {...props} />); generic 'remove all spreads' visitors that do not distinguish SpreadElement from RestElement and JSXSpreadAttribute.
Related errors
- Tried to remove ${node.type} from parent of type ${node.pare
- Tried to remove ${node.type} from parent of type ${node.pare
- Cannot perform a remove mutation on node of type ${node.type
- Could not find target in array of `${node.parent.type}.${key
- Attempted to mutate a `${node.type}.${key}` on a deleted nod
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/ccb3178445ab16d1.
Report an issue: GitHub.