facebook/flow · error · InvalidStatementError
Expected to find the target
Error message
Expected to find the target "${target.type}" on the "${result.parent.type}.${result.key}", but found a different node. This likely means that you attempted to mutate around the target after it was deleted/replaced. What it means
InvalidStatementError thrown by getStatementParent's final identity check: for single-key parents (e.g. `IfStatement.consequent`, `LabeledStatement.body`, loop bodies) it verifies `parent[key] === target`. If a previous mutation in the same run already replaced that slot, the slot now holds a different node and the error explicitly hints that you mutated around a target after it was deleted/replaced.
Solutions
- Anchor follow-up mutations on the replacement node you installed, not the original.
- Issue at most one statement mutation per single-child slot per run.
- When both a replace and insert are needed, use one replaceStatementWithMany with the full final statement list.
Example fix
// before mutate.replaceNode(ifStmt.consequent, newBlock); mutate.insertStatementBefore(oldConsequent, extra); // slot now holds newBlock - throws // after mutate.replaceStatementWithMany(ifStmt.consequent, [extra, ...oldConsequentBody]);
Defensive patterns
Strategy: validation
Validate before calling
function slotStillHoldsTarget(anchor, key): boolean {
return anchor.parent[key] === anchor;
}
if (slotStillHoldsTarget(anchor, 'consequent')) {
mutate.insertStatementBefore(anchor, stmt);
} Try / catch
try {
mutate.insertStatementBefore(anchor, stmt);
} catch (e) {
if (e.name === 'InvalidStatementError' && /found a different node/.test(e.message)) {
// slot was replaced earlier in this run - anchor on the replacement instead
} else throw e;
} Prevention
- After replacing a single-child slot, use the replacement node for follow-up anchors.
- One statement mutation per slot per run.
- Compose final state with replaceStatementWithMany instead of chained edits.
When it happens
Trigger: replaceNode(ifStmt.consequent, x) followed by insertStatementBefore(originalConsequent, ...) in the same run; any second statement mutation anchored on a single-child slot that an earlier replace already swapped.
Common situations: Chained codemod visitors both targeting the same if-branch or loop body; inserting around a statement that a previous visitor replaced with a block.
Related errors
- Could not find target in array of
- Attempted to insert a statement into
- Could not find target in array of
- Expected to find a valid statement parent, but found a…
- Expected to find the
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/6b17a6c136834b56.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-transform/src/transform/mutations/utils/getStatementParent.js:142
key: 'body',
targetIndex: getAssertedIndex('body', parent.body),
};
}
}
throw new InvalidStatementError(
`Expected to find a valid statement parent, but found a parent of type "${parent.type}".`,
);
})();
if (
// array insertions are already validated by the getAssertedIndex function
result.targetIndex == null &&
// $FlowExpectedError[prop-missing]
// $FlowFixMe[invalid-compare]
result.parent[result.key] !== target
) {
throw new InvalidStatementError(
`Expected to find the target "${target.type}" on the "${result.parent.type}.${result.key}", but found a different node. ` +
'This likely means that you attempted to mutate around the target after it was deleted/replaced.',
);
}
return result;
}
View on GitHub (pinned to f88ac94bcf)