facebook/flow · error
Invalid Mutation: Tried to mutate an elements array with an…
Error message
Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length} What it means
astArrayMutationHelpers asserts index bounds (0 <= index < array.length) before every element mutation (replaceInArray, removal, insertion helpers). An out-of-range or negative index throws with the offending index and array size, indicating the caller's positional bookkeeping no longer matches the array being mutated.
Solutions
- Recompute the index immediately before each mutation (e.g. via array.indexOf(node)) instead of caching it.
- Mutate a snapshot plan but resolve indices at apply time against the current array, or rebuild arrays functionally (map/filter) instead of positional splice.
- Check bounds before calling: if (i < 0 || i >= arr.length) skip/log.
- For removals, prefer identity-based removal (find then filter) over index-based removal.
Example fix
// before const idx = body.indexOf(target); forEachOtherNode(() => replaceInArray(body, idx, newNode)); // body changed meanwhile // after const idx = body.indexOf(target); // resolve at apply time if (idx >= 0 && idx < body.length) replaceInArray(body, idx, newNode);
Defensive patterns
Strategy: validation
Validate before calling
// Bounds-check before mutating
function inBounds(array, index) {
return Number.isInteger(index) && index >= 0 && index < array.length;
}
if (inBounds(body, idx)) replaceInArray(body, idx, newNode); Type guard
function isValidIndex(array, index) {
return Number.isInteger(index) && index >= 0 && index < array.length;
} Prevention
- Resolve indices at apply time (indexOf the node), never cache them across mutations.
- Prefer identity-based rebuilds (map/filter) over positional splices in codemods.
When it happens
Trigger: Calling array-mutation helpers with an index computed before earlier mutations shifted/removed elements (stale index), an index taken from a different array, or an off-by-one (e.g. using array.length as the last index instead of length-1).
Common situations: Codemods that remove several nodes while holding precomputed indices; batched edits applied to an AST that was rebuilt mid-loop; transforms that copy an index across traversal boundaries where sibling counts differ.
Related errors
- Cannot insert array into non-array parent type
- Expected parent node to be set on
- No visitor keys found for node type
- SimpleTransform: invalid array result for root node
- SimpleTransform.transformProgram: Expected program node.
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/964cd7a6f1d62d3b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-parser/oxidized-src/transform/astArrayMutationHelpers.js:13
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
function assertArrayBounds<T>(array: ReadonlyArray<T>, index: number): void {
if (index < 0 || index >= array.length) {
throw new Error(
`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`,
);
}
}
export function arrayIsEqual(
a1: ReadonlyArray<unknown>,
a2: ReadonlyArray<unknown>,
): boolean {
if (a1 === a2) {
return true;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {View on GitHub (pinned to f88ac94bcf)