facebook/flow · error · Error
Unexpected selector
Error message
Unexpected selector ${parsedSelector.value} What it means
Plain Error thrown by flow-transform's traverse when a visitor key used as an esquery identifier selector is not a known Flow AST node type (validated against `FlowVisitorKeys` from flow-parser). Bare identifiers in selectors are interpreted as node type names, so a typo or a Babel/TypeScript/ESLint-only type name fails immediately when the NodeEventGenerator registers listeners.
Solutions
- Use Flow ESTree node type names - dump the AST with hermes-parser/flow-parser to confirm exact type strings.
- Map common renames: StringLiteral/NumberLiteral -> Literal, ClassProperty -> PropertyDefinition, TSTypeAnnotation -> Flow type annotation nodes.
- For non-type selectors use esquery syntax that does not require an identifier (attribute selectors, `*`).
- Check the name against FlowVisitorKeys (exported from flow-parser) at startup.
Example fix
// before
traverseWithContext(code, ast, scope, () => ({}), ctx => ({
ClassProperty(node) {}, // not a Flow ESTree type - throws
}));
// after
traverseWithContext(code, ast, scope, () => ({}), ctx => ({
PropertyDefinition(node) {},
})); Defensive patterns
Strategy: validation
Validate before calling
import {FlowVisitorKeys} from 'flow-parser';
function validateVisitorKeys(selectors) {
for (const sel of Object.keys(selectors)) {
const base = sel.replace(/:exit$/u, '');
// bare identifiers (no combinators/attributes) must be Flow node types
if (/^[A-Za-z]+$/.test(base) && !(base in FlowVisitorKeys)) {
throw new Error(`visitor key '${sel}' is not a Flow AST node type`);
}
}
}
validateVisitorKeys(selectors); // before traverseWithContext(...) Type guard
function isFlowNodeType(name: string): boolean {
const {FlowVisitorKeys} = require('flow-parser');
return name in FlowVisitorKeys;
} Prevention
- Use flow-estree type names (Literal, PropertyDefinition), not Babel/TS ones.
- Dump the AST with hermes-parser to confirm exact type strings.
- Validate bare-identifier visitor keys against FlowVisitorKeys at startup or in tests.
When it happens
Trigger: `traverse(ast, {CallExpresion(node) {}})` (typo) or using Babel/TS names like `TSTypeAnnotation`, `ClassProperty`, `StringLiteral` - Flow's ESTree AST uses different names (e.g. `Literal`, `PropertyDefinition`). Thrown when traverse() builds its event generator, before any traversal.
Common situations: Porting ESLint rules or Babel plugin visitors to flow-transform; copying selector examples written against estree/babel node type lists; version drift where a node type was renamed in flow-estree.
Related errors
- Syntax error in selector
- Attempted to insert a statement into
- Cannot perform a remove mutation on node of type
- Could not find target in array of
- Could not find target in array of
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/51c2aa16635bdfa8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/flow-transform/src/traverse/NodeEventGenerator.js:78
let result = [...new Set(arrays[0])];
for (const array of arrays.slice(1)) {
result = result.filter(x => array.includes(x));
}
return result;
}
/**
* Gets the possible types of a selector
* @param parsedSelector An object (from esquery) describing the matching behavior of the selector
* @returns The node types that could possibly trigger this selector, or `null` if all node types could trigger it
*/
function getPossibleTypes(parsedSelector: Selector): ?Array<ESNode['type']> {
switch (parsedSelector.type) {
case 'identifier':
if (!(parsedSelector.value in FlowVisitorKeys)) {
throw new Error(`Unexpected selector ${parsedSelector.value}`);
}
// $FlowExpectedError[incompatible-type]
return [parsedSelector.value];
case 'matches': {
const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
const typesForComponentsNonNull = typesForComponents.filter(Boolean);
if (typesForComponents.length === typesForComponentsNonNull.length) {
return union(...typesForComponentsNonNull);
}
return null;
}
case 'compound': {
const typesForComponents = parsedSelector.selectors
.map(getPossibleTypes)
.filter(Boolean);View on GitHub (pinned to f88ac94bcf)