facebook/flow · error
Expected parent node to be set on
Error message
Expected parent node to be set on "${target.type}" What it means
The node-mutation helpers locate a node by walking its parent's visitor keys, so the target must have .parent set. getNodeOnParent throws when target.parent is null: the node is detached — raw flow-parser parse() output does not set parent pointers; parents are attached during SimpleTransform traversal.
Solutions
- Do mutations inside a SimpleTransform visitor — traversal sets parent pointers, and the visitor hands you nodes with parents attached.
- Or set node.parent yourself as you attach children when building/mutating trees manually.
- Check `target.parent != null` before calling and fail with your own diagnostic if detached.
Example fix
// before
const ast = parse(code);
replaceNodeOnParent(ast.body[0].expression, newNode); // parent is null -> throws
// after
SimpleTransform.transformProgram(ast, {
transform(node) {
if (node.type === 'ThatExpression') return newNode; // parents set by traversal
return node;
},
}); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure parents exist before mutating
function nodesHaveParents(program) {
let ok = true;
(function walk(n) {
if (n == null || typeof n !== 'object') return;
if (typeof n.type === 'string' && n.parent == null && n !== program) ok = false;
for (const k of Object.keys(n)) if (k !== 'parent') walk(n[k]);
})(program);
return ok;
} Type guard
function isAttachedToTree(node) {
return node != null && node.parent != null;
} Prevention
- Mutate only inside SimpleTransform visitors, where parents are set.
- If building trees manually, assign .parent when attaching children.
- Remember flow-parser's parse() does not set parents — unlike babel/traverse.
When it happens
Trigger: Calling replaceNodeOnParent/removeNodeOnParent on a node obtained directly from parse() or constructed by hand, outside a transform traversal, so no parent pointer was ever assigned.
Common situations: Custom AST tooling that parses, then tries to mutate nodes in place without running a transform; tests that build node literals manually; code ported from libraries whose parser sets parents (babel/traverse sets them, flow-parser does not).
Related errors
- Cannot insert array into non-array parent type
- Invalid Mutation: Tried to mutate an elements array with an…
- 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/252a09718a65e6b8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-parser/oxidized-src/transform/astNodeMutationHelpers.js:42
function getParentKey(
target: ESNode,
parent: ESNode,
visitorKeys?: ?VisitorKeysType,
): Readonly<
| {
type: 'single',
node: ESNode,
key: string,
}
| {
type: 'array',
node: ESNode,
key: string,
targetIndex: number,
},
> {
if (parent == null) {
throw new Error(`Expected parent node to be set on "${target.type}"`);
}
for (const key of getVisitorKeys(parent, visitorKeys)) {
if (
isNode(
// $FlowExpectedError[prop-missing]
parent[key],
)
) {
// $FlowFixMe[invalid-compare]
if (parent[key] === target) {
return {type: 'single', node: parent, key};
}
} else if (
Array.isArray(
// $FlowExpectedError[prop-missing]
parent[key],
)
) {View on GitHub (pinned to f88ac94bcf)