facebook/flow · error · InvalidStatementError
Could not find target in array of
Error message
Could not find target in array of `${parent.type}.${key}`. What it means
InvalidStatementError thrown by getStatementParent's `getAssertedIndex` helper. For array-based statement parents (Program.body, BlockStatement.body, SwitchCase.consequent) the anchor statement must be present by identity (`indexOf`) in that array. If the anchor was already removed/replaced by an earlier mutation in the run, or is a stale/detached node object, indexOf returns -1 and this error fires.
Solutions
- One mutation per anchor statement per run; insert relative to a statement you have not mutated.
- Re-query the current AST (re-traverse) between dependent mutation batches.
- Assert `anchor.parent.body.includes(anchor)` before issuing the mutation.
Example fix
// before mutate.removeNode(stmt); mutate.insertStatementAfter(stmt, replacement); // stmt no longer in body - throws // after mutate.replaceStatementWithMany(stmt, [replacement]);
Defensive patterns
Strategy: validation
Validate before calling
function anchorStillInBody(anchor): boolean {
const p = anchor.parent;
const arr = p.type === 'SwitchCase' ? p.consequent : p.body;
return Array.isArray(arr) && arr.includes(anchor);
}
if (anchorStillInBody(anchor)) {
mutate.insertStatementBefore(anchor, stmt);
} Try / catch
try {
mutate.insertStatementBefore(anchor, stmt);
} catch (e) {
if (e.name === 'InvalidStatementError' && /Could not find target/.test(e.message)) {
// anchor was already removed/replaced - re-traverse and pick a live anchor
} else throw e;
} Prevention
- Anchor inserts on statements you have not mutated in this run.
- Re-traverse between dependent mutation batches.
- Prefer replaceStatementWithMany for remove-plus-insert sequences.
When it happens
Trigger: insertStatementBefore/After or statement removal anchored on a statement that a previous mutation in the same transform run already deleted or replaced; using a node captured from an earlier traversal.
Common situations: Codemods that both remove statements and insert around them; chained visitors mutating the same BlockStatement body; holding node references across transform passes.
Related errors
- Expected to find the target
- 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/34f234df54064d00.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-transform/src/transform/mutations/utils/getStatementParent.js:60
for (const key of invalidKeys) {
// $FlowExpectedError[prop-missing]
const value = parentWithType[key];
if (
// $FlowFixMe[invalid-compare]
value === target ||
(Array.isArray(value) && value.includes(target))
) {
throw new InvalidStatementError(
`Attempted to insert a statement into \`${parentWithType.type}.${key}\`.`,
);
}
}
}
function getAssertedIndex(key: string, arr: ReadonlyArray<unknown>): number {
const idx = arr.indexOf(target);
if (idx === -1) {
throw new InvalidStatementError(
`Could not find target in array of \`${parent.type}.${key}\`.`,
);
}
return idx;
}
const parent = target.parent;
const result: StatementParent = (() => {
switch (parent.type) {
case 'IfStatement': {
assertValidStatementLocation(parent, 'test');
const key = parent.consequent === target ? 'consequent' : 'alternate';
return {type: 'single', parent, key};
}
case 'LabeledStatement': {
assertValidStatementLocation(parent, 'label');
return {type: 'single', parent, key: 'body'};View on GitHub (pinned to f88ac94bcf)