facebook/flow · error · InvalidRemovalError
Cannot perform a remove mutation on node of type
Error message
Cannot perform a remove mutation on node of type ${node.type} What it means
InvalidRemovalError thrown by removeNode's `getRemovalParent` when the node's type is not in the hardcoded whitelist of removable types (class members, enum members, component params, function params, object-type members, Property, ImportDeclaration, JSXAttribute, FunctionTypeParam, Identifier, RestElement, SpreadElement, etc.). Statements like ExpressionStatement, VariableDeclaration, or literals cannot be removed with removeNode.
Solutions
- To delete a statement, use `mutate.replaceStatementWithMany(stmt, [])` instead of removeNode.
- Remove an ancestor that is whitelisted: e.g. remove the Property for an object member, the VariableDeclarator's Property/parent for declarations, or an ImportDeclaration (which IS supported under Program).
- Check RemoveNode.js's switch statement for the current whitelist before choosing the node to remove.
Example fix
// before mutate.removeNode(exprStatement); // 'Cannot perform a remove mutation on node of type ExpressionStatement' // after mutate.replaceStatementWithMany(exprStatement, []);
Defensive patterns
Strategy: type-guard
Validate before calling
const REMOVABLE_TYPES = new Set([
'PropertyDefinition', 'MethodDefinition',
'EnumBooleanMember', 'EnumDefaultedMember', 'EnumNumberMember', 'EnumStringMember',
'ComponentParameter', 'ComponentTypeParameter',
'AssignmentPattern', 'ArrayPattern', 'ObjectPattern', 'FunctionTypeParam',
'JSXAttribute', 'ImportDeclaration',
'ObjectTypeCallProperty', 'ObjectTypeIndexer', 'ObjectTypeInternalSlot',
'ObjectTypeProperty', 'ObjectTypeSpreadProperty', 'Property',
'Identifier', 'RestElement', 'SpreadElement',
]);
if (REMOVABLE_TYPES.has(node.type)) {
mutate.removeNode(node);
} else {
mutate.replaceStatementWithMany(node, []); // statements: replace with nothing
} Type guard
function canRemoveNode(node): boolean {
return new Set([
'PropertyDefinition', 'MethodDefinition', 'Property', 'JSXAttribute',
'ImportDeclaration', 'Identifier', 'RestElement', 'SpreadElement',
'AssignmentPattern', 'ArrayPattern', 'ObjectPattern', 'FunctionTypeParam',
'ComponentParameter', 'ComponentTypeParameter',
'EnumBooleanMember', 'EnumDefaultedMember', 'EnumNumberMember', 'EnumStringMember',
'ObjectTypeCallProperty', 'ObjectTypeIndexer', 'ObjectTypeInternalSlot',
'ObjectTypeProperty', 'ObjectTypeSpreadProperty',
]).has(node.type);
} Prevention
- removeNode is for members/params/elements - not statements or expressions.
- Delete statements with replaceStatementWithMany(stmt, []).
- Mirror the whitelist from RemoveNode.js in a canRemoveNode guard.
When it happens
Trigger: `mutate.removeNode(node)` where node is an ExpressionStatement, CallExpression, Literal, VariableDeclaration, or any type not handled by the switch in RemoveNode.js - the default branch throws immediately with the node's type.
Common situations: Trying to delete a whole statement or expression directly; the most common codemod mistake when removing a call statement instead of the enclosing statement.
Related errors
- Could not find target in array of
- Tried to remove from parent of type . However can only be…
- Attempted to insert a statement into
- Could not find target in array of
- Expected to find a valid statement parent, but found a…
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/3add287daa726148.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-transform/src/transform/mutations/RemoveNode.js:270
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 = (() => {
// $FlowExpectedError[prop-missing]
const arr = node.parent[key];
const idx = arr.indexOf(node);
// $FlowFixMe[invalid-compare]
if (idx === -1) {
throw new InvalidRemovalError(
`Could not find target in array of \`${node.parent.type}.${key}\`.`,
);
}
return idx;
})();
View on GitHub (pinned to f88ac94bcf)