facebook/flow · error · Error
No visitor keys found for node type "${node.type}".
Error message
No visitor keys found for node type "${node.type}". What it means
NodeEventGenerator configures esquery with FlowVisitorKeys and a fallback that throws when traversal reaches a node whose type has no visitor-keys entry. The error means the AST being traversed contains a node type the Flow visitor-key table does not know, so esquery cannot enumerate the node's children. It almost always indicates an AST produced by something other than the matching Flow parser version (Babel/TypeScript ASTs, hand-built nodes, or a flow-ast version out of sync with flow-transform).
Source
Thrown at packages/flow-transform/src/traverse/NodeEventGenerator.js:38
type ParsedSelector = Readonly<{
/** The string that was parsed into this selector */
rawSelector: string,
/** `true` if this should be emitted when exiting the node rather than when entering */
isExit: boolean,
/** An object (from esquery) describing the matching behavior of the selector */
parsedSelector: Selector,
/** A list of node types that could possibly cause the selector to match, or `null` if all node types could cause a match */
listenerTypes: ?Array<ESNode['type']>,
/** The total number of classes, pseudo-classes, and attribute queries in this selector */
attributeCount: number,
/** The total number of identifier queries in this selector */
identifierCount: number,
}>;
const ESQUERY_OPTIONS: ESQueryOptions = Object.freeze({
visitorKeys: FlowVisitorKeys,
fallback: (node: ESNode) => {
throw new Error(`No visitor keys found for node type "${node.type}".`);
},
});
/**
* Computes the union of one or more arrays
* @param arrays One or more arrays to union
* @returns The union of the input arrays
*/
function union<T>(...arrays: Array<Array<T>>): Array<T> {
return [...new Set(arrays.flat())];
}
/**
* Computes the intersection of one or more arrays
* @param arrays One or more arrays to intersect
* @returns The intersection of the input arrays
*/
function intersection<T>(...arrays: Array<Array<T>>): Array<T> {View on GitHub (pinned to d1341dac89)
Solutions
- Parse the source with the Flow parser that pairs with your flow-transform version before traversing.
- Align versions: make flow-ast/flow-parser and flow-transform come from the same release so FlowVisitorKeys covers every emitted node type.
- If you inject custom nodes, strip or replace them before calling traverse().
- Log node.type in a pre-pass and compare against FlowVisitorKeys to identify the offending node.
Example fix
// before
import {parse} from '@babel/parser';
const ast = parse(code, {sourceType: 'module'});
traverse(ast, visitor); // Babel AST -> unknown node types
// after
import {parse} from 'flow-parser';
const ast = parse(code);
traverse(ast, visitor); // Flow AST matches FlowVisitorKeys Defensive patterns
Strategy: validation
Validate before calling
import {FlowVisitorKeys} from 'flow-ast';
function astOnlyHasKnownNodeTypes(ast) {
const unknown = new Set();
(function walk(node) {
if (node == null || typeof node.type !== 'string') return;
if (!(node.type in FlowVisitorKeys)) unknown.add(node.type);
for (const key of FlowVisitorKeys[node.type] ?? []) {
const child = node[key];
if (Array.isArray(child)) child.forEach(walk);
else if (child != null && typeof child.type === 'string') walk(child);
}
})(ast);
return unknown.size === 0 ? null : [...unknown];
} Type guard
/** True when the node type is traversable by flow-transform (present in FlowVisitorKeys). */
import {FlowVisitorKeys} from 'flow-ast';
function isKnownNodeType(node) {
return node != null && typeof node.type === 'string' && node.type in FlowVisitorKeys;
} Try / catch
try {
traverse(ast, visitor);
} catch (err) {
if (err instanceof Error && err.message.startsWith('No visitor keys found')) {
// AST contains a non-Flow node; reparse with the Flow parser before traversing
ast = flowParse(originalSource);
traverse(ast, visitor);
} else {
throw err;
}
} Prevention
- Always parse input with the Flow parser paired with your flow-transform version.
- Pin flow-ast/flow-parser/flow-transform to the same release in package.json.
- Strip custom/synthetic nodes before traversal.
When it happens
Trigger: Calling traverse() on a Babel or TypeScript AST (e.g. TSTypeAnnotation, TSAsExpression nodes) instead of one parsed by the Flow parser; traversing an AST that contains custom synthetic nodes injected by another tool; using a flow-ast package version whose visitor keys lack types that flow-transform's traversal expects.
Common situations: Sharing codemod code between Babel and Flow projects and accidentally passing a Babel AST into flow-transform's traverse; upgrading one of flow-ast/flow-parser/flow-transform independently so the visitor-key table and emitted node types drift; inserting custom metadata nodes into the tree before traversal.
Related errors
- Unexpected selector ${parsedSelector.value}
- Syntax error in selector "${rawSelector}" at position ${err.
- No visitor keys found for node type "${node.type}".
- Attempted to mutate a `${node.type}.${key}` on a deleted nod
- Attempted to mutate a `${node.type}.${key}` when it has alre
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/8b8ccebe382948ea.
Report an issue: GitHub.