{"record":{"id":"8b8ccebe382948ea","repo":"facebook/flow","slug":"no-visitor-keys-found-for-node-type-node-type-8b8cce","errorCode":null,"errorMessage":"No visitor keys found for node type \"${node.type}\".","messagePattern":"No visitor keys found for node type \"(.+?)\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/flow-transform/src/traverse/NodeEventGenerator.js","lineNumber":38,"sourceCode":"type ParsedSelector = Readonly<{\n  /** The string that was parsed into this selector */\n  rawSelector: string,\n  /** `true` if this should be emitted when exiting the node rather than when entering */\n  isExit: boolean,\n  /** An object (from esquery) describing the matching behavior of the selector */\n  parsedSelector: Selector,\n  /** A list of node types that could possibly cause the selector to match, or `null` if all node types could cause a match */\n  listenerTypes: ?Array<ESNode['type']>,\n  /** The total number of classes, pseudo-classes, and attribute queries in this selector */\n  attributeCount: number,\n  /** The total number of identifier queries in this selector */\n  identifierCount: number,\n}>;\n\nconst ESQUERY_OPTIONS: ESQueryOptions = Object.freeze({\n  visitorKeys: FlowVisitorKeys,\n  fallback: (node: ESNode) => {\n    throw new Error(`No visitor keys found for node type \"${node.type}\".`);\n  },\n});\n\n/**\n * Computes the union of one or more arrays\n * @param arrays One or more arrays to union\n * @returns The union of the input arrays\n */\nfunction union<T>(...arrays: Array<Array<T>>): Array<T> {\n  return [...new Set(arrays.flat())];\n}\n\n/**\n * Computes the intersection of one or more arrays\n * @param arrays One or more arrays to intersect\n * @returns The intersection of the input arrays\n */\nfunction intersection<T>(...arrays: Array<Array<T>>): Array<T> {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/facebook/flow/blob/d1341dac899a79c027762f6b423d896045287620/packages/flow-transform/src/traverse/NodeEventGenerator.js#L20-L56","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nimport {parse} from '@babel/parser';\nconst ast = parse(code, {sourceType: 'module'});\ntraverse(ast, visitor); // Babel AST -> unknown node types\n\n// after\nimport {parse} from 'flow-parser';\nconst ast = parse(code);\ntraverse(ast, visitor); // Flow AST matches FlowVisitorKeys","handlingStrategy":"validation","validationCode":"import {FlowVisitorKeys} from 'flow-ast';\n\nfunction astOnlyHasKnownNodeTypes(ast) {\n  const unknown = new Set();\n  (function walk(node) {\n    if (node == null || typeof node.type !== 'string') return;\n    if (!(node.type in FlowVisitorKeys)) unknown.add(node.type);\n    for (const key of FlowVisitorKeys[node.type] ?? []) {\n      const child = node[key];\n      if (Array.isArray(child)) child.forEach(walk);\n      else if (child != null && typeof child.type === 'string') walk(child);\n    }\n  })(ast);\n  return unknown.size === 0 ? null : [...unknown];\n}","typeGuard":"/** True when the node type is traversable by flow-transform (present in FlowVisitorKeys). */\nimport {FlowVisitorKeys} from 'flow-ast';\n\nfunction isKnownNodeType(node) {\n  return node != null && typeof node.type === 'string' && node.type in FlowVisitorKeys;\n}","tryCatchPattern":"try {\n  traverse(ast, visitor);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('No visitor keys found')) {\n    // AST contains a non-Flow node; reparse with the Flow parser before traversing\n    ast = flowParse(originalSource);\n    traverse(ast, visitor);\n  } else {\n    throw err;\n  }\n}","preventionTips":["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."],"tags":["flow-transform","traverse","esquery","visitor-keys","version-mismatch"],"backgroundTag":"unknown-ast-node-type","analyzedSha":"d1341dac899a79c027762f6b423d896045287620","analyzedAt":"2026-08-17T00:07:02.212Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}