facebook/flow · error · InvalidRemovalError
Could not find target in array of `${node.parent.type}.${key
Error message
Could not find target in array of `${node.parent.type}.${key}`. What it means
After computing which array key holds the node, RemoveNode does parent[key].indexOf(node) to find the splice index; a -1 result means the parent pointer claims a relationship the array no longer reflects, and this InvalidRemovalError is thrown. Like the 'direct child' invariants elsewhere, it indicates the AST was mutated outside the mutation API or the node is a copy whose parent points at the original array. The message includes the parent type and key to narrow the search.
Source
Thrown at packages/flow-transform/src/transform/mutations/RemoveNode.js:282
'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;
})();
return {
type: 'array',
parent: node.parent,
key,
targetIndex,
};
}
export function performRemoveNodeMutation(
mutationContext: MutationContext,
mutation: RemoveNodeMutation,
): ESNode {View on GitHub (pinned to d1341dac89)
Solutions
- Do all structural changes through the mutation API: never splice parent arrays by hand during a transform
- Re-parse or re-traverse to get a pristine AST before each new batch of mutations
- When cloning nodes, clear their parent pointers so stale links fail fast instead of mismatching
Example fix
// before program.body.splice(2, 1); // raw splice mutations.push(removeNode(stmt)); // stmt no longer in body -> throws // after mutations.push(removeNode(program.body[2])); // engine owns the splice
Defensive patterns
Strategy: try-catch
Validate before calling
function isNodeInParentArray(node, key) {
const arr = node.parent && node.parent[key];
return Array.isArray(arr) && arr.indexOf(node) !== -1;
} Type guard
const isInParentArray = (node, key) => node.parent != null && Array.isArray(node.parent[key]) && node.parent[key].includes(node);
Try / catch
try {
mutations.push(removeNodeMutation(node));
} catch (e) {
if (e.message.includes('Could not find target in array')) {
// stale tree: re-parse the original source and re-locate the node instead of continuing
throw new Error('AST out of sync with mutation API; re-parse before mutating');
}
throw e;
} Prevention
- Never splice parent arrays by hand during a transform; route all structural edits through the mutation API
- Clear parent pointers when cloning nodes so stale links fail loudly
- Re-parse between transform passes rather than reusing a mutated tree
When it happens
Trigger: Splicing an array manually (e.g. program.body.splice(...) inside a visitor) and then calling removeNodeMutation on a node from that region; removing a cloned node whose .parent still references the original tree's parent.
Common situations: Mixing direct AST manipulation with the mutation API in one pass; deep-cloning subtrees but keeping inherited parent links; running a second mutation batch over a tree already mutated by raw code.
Related errors
- Expected to find the ${target.type} as a direct child of the
- Tried to remove ${node.type} from parent of type ${node.pare
- 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
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/72391b272bfd0882.
Report an issue: GitHub.