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
getVisitorKeys is the single lookup every traversal and transform uses to enumerate a node's child keys; it indexes the visitor-keys map by node.type and throws when there is no entry. The default map is FlowVisitorKeys, which knows exactly the node types flow-parser emits (including Flow type nodes). An unknown type therefore means the AST was not produced by flow-parser, or contains injected or custom nodes.
Source
Thrown at packages/flow-parser/oxidized-src/traverse/getVisitorKeys.js:31
import type {ESNode} from 'flow-estree';
import type {VisitorKeys as VisitorKeysType} from '../generated/ESTreeVisitorKeys';
import FlowVisitorKeys from '../generated/ESTreeVisitorKeys';
export function isNode(thing: unknown) /*: implies thing is {readonly [string]: unknown} */ {
return (
typeof thing === 'object' && thing != null && typeof thing.type === 'string'
);
}
export type {VisitorKeysType};
export function getVisitorKeys<T extends ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
) /*: ReadonlyArray<keyof T> */ {
const keys = (visitorKeys ?? FlowVisitorKeys)[node.type];
if (keys == null) {
throw new Error(`No visitor keys found for node type "${node.type}".`);
}
// $FlowExpectedError[prop-missing]
return keys;
}
View on GitHub (pinned to d1341dac89)
Solutions
- Only traverse ASTs produced by the same flow-parser version
- Pass a custom visitorKeys map via the traversal or transform options that covers your AST dialect's node types
- Upgrade flow-parser so FlowVisitorKeys includes the newest node types it emits
Example fix
// before
traverse(babelAst, visitor); // 'File'/'TS*' types unknown -> throws
// after
const flowAst = require('flow-parser').parse(source, {});
traverse(flowAst, visitor); Defensive patterns
Strategy: validation
Validate before calling
import {FlowVisitorKeys} from 'flow-parser'; // or the traverse export your version ships
function assertKnownTypes(ast) {
const stack = [ast];
while (stack.length) {
const n = stack.pop();
if (!(n.type in FlowVisitorKeys)) {
throw new Error("AST contains non-Flow node type '" + n.type + "'; reparse with flow-parser");
}
for (const k of FlowVisitorKeys[n.type]) {
const v = n[k];
if (v && typeof v.type === 'string') stack.push(v);
else if (Array.isArray(v)) for (const c of v) if (c && typeof c.type === 'string') stack.push(c);
}
}
} Type guard
const isKnownNodeType = (type, visitorKeys) => type in (visitorKeys || FlowVisitorKeys);
Try / catch
try {
traverse(ast, visitor);
} catch (e) {
if (e.message.startsWith('No visitor keys found')) {
throw new Error('AST dialect mismatch: pass a matching visitorKeys option or reparse with flow-parser');
}
throw e;
} Prevention
- Only feed ASTs from the same flow-parser version into its traverse and transform
- When handling other dialects, pass an explicit visitorKeys map covering every node type you emit
- Validate node types up front when accepting ASTs from external tools or serialized caches
When it happens
Trigger: Feeding an AST from another parser (Babel, Acorn, espree, typescript-eslint) into flow-parser's traverse or SimpleTransform: node types like 'File', 'TSUnionType', 'ChainExpression' have no FlowVisitorKeys entry; injecting custom node types from a visitor; using an outdated flow-parser whose map predates a newer node type.
Common situations: Toolchains that mix parsers (lint with espree, transform with flow-parser) and pass ASTs between them; codemods run on TypeScript-in-Flow repos; upgrading one tool but not flow-parser.
Related errors
- SimpleTransform: invalid array result for root node
- SimpleTransform.transformProgram: Expected program node.
- Expected parent node to be set on "${target.type}"
- Expected to find the ${target.type} as a direct child of the
- Cannot insert array into non-array parent type: ${parent.typ
AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17).
Data as JSON: /api/errors/bf6df1fe354931bd.
Report an issue: GitHub.