{"record":{"id":"964cd7a6f1d62d3b","repo":"facebook/flow","slug":"invalid-mutation-tried-to-mutate-an-elements-arra","errorCode":null,"errorMessage":"Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}","messagePattern":"Invalid Mutation: Tried to mutate an elements array with an out of bounds index\\. Index: (.+?), Array Size: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/flow-parser/oxidized-src/transform/astArrayMutationHelpers.js","lineNumber":13,"sourceCode":"/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @flow strict\n * @format\n */\n\nfunction assertArrayBounds<T>(array: ReadonlyArray<T>, index: number): void {\n  if (index < 0 || index >= array.length) {\n    throw new Error(\n      `Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`,\n    );\n  }\n}\n\nexport function arrayIsEqual(\n  a1: ReadonlyArray<unknown>,\n  a2: ReadonlyArray<unknown>,\n): boolean {\n  if (a1 === a2) {\n    return true;\n  }\n\n  if (a1.length !== a2.length) {\n    return false;\n  }\n\n  for (let i = 0; i < a1.length; i++) {","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/facebook/flow/blob/d1341dac899a79c027762f6b423d896045287620/packages/flow-parser/oxidized-src/transform/astArrayMutationHelpers.js#L1-L31","documentation":"astArrayMutationHelpers wraps every array-mutating helper (replace/insert/remove at index) with assertArrayBounds, which requires 0 <= index < array.length. The throw message reports both the offending index and the current array size, so a size smaller than expected is itself a clue: the array was already mutated. It prevents silent no-ops or undefined holes in AST arrays.","triggerScenarios":"Calling an array mutation helper with a negative index, an index equal to length, or an index computed against an older, longer version of the same array after earlier mutations shrank it.","commonSituations":"Codemods that snapshot indices up front and apply mutations in a loop (each removal shifts subsequent indices); off-by-one bugs using array.length as an insert position instead of the valid range for the specific helper.","solutions":["Recompute the index immediately before the mutation, ideally via array.indexOf(node) on the live array","Mutate from the highest index to the lowest so earlier mutations cannot shift pending indices","Add a bounds assert in your own code comparing index against the array length at call time"],"exampleFix":"// before\nconst idx = stmts.indexOf(target);\n// ...other mutations shrink stmts...\nreplaceInArray(stmts, idx, newNode);\n\n// after\nconst idx = stmts.indexOf(target); // recompute right before use\nif (idx < 0 || idx >= stmts.length) throw new Error('stale index');\nreplaceInArray(stmts, idx, newNode);","handlingStrategy":"validation","validationCode":"function assertInBounds(array, index) {\n  if (!Number.isInteger(index) || index < 0 || index >= array.length) {\n    throw new RangeError('Index ' + index + ' invalid for array of ' + array.length);\n  }\n}\nassertInBounds(stmts, idx);","typeGuard":"const inBounds = (array, index) =>\n  Number.isInteger(index) && index >= 0 && index < array.length;","tryCatchPattern":"try {\n  replaceInArray(arr, idx, node);\n} catch (e) {\n  if (e.message.includes('out of bounds index')) {\n    idx = arr.indexOf(nodeToFind); // re-resolve against the live array, retry once\n  } else throw e;\n}","preventionTips":["Prefer indexOf(node) on the live array over cached numeric indices","Apply array mutations back-to-front so shifts cannot invalidate pending indices","Assert bounds at your own boundary so failures point at your code, not the helper"],"tags":["ast","mutation","array","index-out-of-bounds"],"backgroundTag":"array-index-out-of-bounds","analyzedSha":"d1341dac899a79c027762f6b423d896045287620","analyzedAt":"2026-08-17T00:07:02.212Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}