facebook/flow · error · InvalidInsertionError
import/export cannot be inserted into a ${insertionParent.pa
Error message
import/export cannot be inserted into a ${insertionParent.parent.type}. What it means
InsertStatement validates insertion sites for module declarations: imports and exports are only legal as direct children of a Program (top level) or of a BlockStatement that is the body of a Flow DeclareModule. isValidModuleDeclarationParent returns false when any node being inserted is an Import or Export declaration and the insertion parent is anything else, and this InvalidInsertionError is the result.
Source
Thrown at packages/flow-transform/src/transform/mutations/InsertStatement.js:63
export function performInsertStatementMutation(
mutationContext: MutationContext,
mutation: InsertStatementMutation,
): ESNode {
mutationContext.assertNotDeleted(
mutation.target,
`Attempted to insert ${mutation.side} a deleted ${mutation.target.type} node. This likely means that you attempted to mutate around the target after it was deleted/replaced.`,
);
const insertionParent = getStatementParent(mutation.target);
// enforce that if we are inserting module declarations - they are being inserted in a valid location
if (
!isValidModuleDeclarationParent(
insertionParent.parent,
mutation.nodesToInsert,
)
) {
throw new InvalidInsertionError(
`import/export cannot be inserted into a ${insertionParent.parent.type}.`,
);
}
mutationContext.markMutation(insertionParent.parent, insertionParent.key);
if (insertionParent.type === 'array') {
const parent: interface {
[string]: ReadonlyArray<DetachedNode<Statement | ModuleDeclaration>>,
} = insertionParent.parent;
switch (mutation.side) {
case 'before': {
parent[insertionParent.key] = astArrayMutationHelpers.insertInArray(
parent[insertionParent.key],
insertionParent.targetIndex,
mutation.nodesToInsert,
);
break;View on GitHub (pinned to d1341dac89)
Solutions
- Anchor the insertion on a top-level statement: walk up from the target until parent.type === 'Program'
- For DeclareModule bodies, ensure the anchor's parent is the DeclareModule's BlockStatement
- If you only need non-module statements inserted, the nested location is fine: drop the import from the batch
Example fix
// before
const target = referencingStmt; // nested in a function body
mutations.push(insertStatement('before', target, [importDecl])); // throws
// after
let top = target;
while (top.parent && top.parent.type !== 'Program') top = top.parent;
mutations.push(insertStatement('before', top, [importDecl])); Defensive patterns
Strategy: validation
Validate before calling
const MODULE_DECLS = new Set(['ImportDeclaration', 'ExportNamedDeclaration', 'ExportDefaultDeclaration', 'ExportAllDeclaration']);
function canInsertHere(target, nodes) {
const p = target.parent;
const parentOk = p.type === 'Program' ||
(p.type === 'BlockStatement' && p.parent && p.parent.type === 'DeclareModule');
const hasModuleDecl = nodes.some(n => MODULE_DECLS.has(n.type));
return !hasModuleDecl || parentOk;
} Type guard
const isModuleDeclaration = (n) => n.type === 'ImportDeclaration' || n.type === 'ExportNamedDeclaration' || n.type === 'ExportDefaultDeclaration' || n.type === 'ExportAllDeclaration';
Try / catch
try {
mutations.push(insertStatement(side, target, nodes));
} catch (e) {
if (e.message.includes('import/export cannot be inserted')) {
// hoist anchor to top level and retry
let top = target;
while (top.parent && top.parent.type !== 'Program') top = top.parent;
mutations.push(insertStatement('before', top, nodes));
} else throw e;
} Prevention
- Anchor import and export insertions on statements whose parent is Program
- Check target.parent.type before issuing insertStatement for module declarations
- In generated codemods, always hoist to the module top level instead of using the reference site
When it happens
Trigger: insertStatementMutation('before' or 'after', target, nodes) where any node in nodes is an ImportDeclaration or Export* and the target's statement parent is a BlockStatement (function or if body), SwitchCase, or another non-Program container.
Common situations: Auto-import codemods (e.g. adding an import for an undefined identifier) anchoring on the first statement that references the identifier, which sits inside a function; inserting an export statement inside a nested block.
Related errors
- import/export cannot be replaced into a ${replacementParent.
- ImportDeclaration should appear when the mode is ES6 and in
- Attempted to mutate a `${node.type}.${key}` on a deleted nod
- Attempted to mutate a `${node.type}.${key}` when it has alre
- Tried to remove ${node.type} from parent of type ${node.pare
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/f9257342882a0f58.
Report an issue: GitHub.