facebook/flow · error · Error
Unexpected selector ${parsedSelector.value}
Error message
Unexpected selector ${parsedSelector.value} What it means
getPossibleTypes() in NodeEventGenerator resolves an identifier-style selector (a bare node-type name such as 'CallExpression') against FlowVisitorKeys; if the name is not a key in that table it throws. In practice the selector comes from the event names your visitor registers, i.e. a visitor key like visitFooBar becomes the selector 'FooBar'. The error therefore means you registered a visitor for a node type that does not exist in the Flow AST vocabulary — usually a TypeScript/Babel-only type or a typo.
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 d1341dac89)
Solutions
- Use exact Flow AST node type names (see flow-ast's FlowVisitorKeys), e.g. visitTSTypeAnnotation -> visitTypeCastExpression or the Flow equivalent; StringLiteral -> Literal.
- Validate each visitor key's node type against FlowVisitorKeys before registering the visitor.
- For misspellings, cross-check the name against the Flow parser output (Object.keys on a parsed node's types).
- If you need TypeScript AST support, use a TS-aware toolchain instead of flow-transform.
Example fix
// before
const visitor = {
visitTSTypeAnnotation(node) {}, // 'TSTypeAnnotation' not in FlowVisitorKeys
};
// after
const visitor = {
visitTypeAnnotation(node) {}, // Flow AST type name
}; Defensive patterns
Strategy: type-guard
Validate before calling
import {FlowVisitorKeys} from 'flow-ast';
function validateVisitorKeys(visitor) {
const invalid = Object.keys(visitor)
.filter(k => k.startsWith('visit'))
.map(k => k.slice('visit'.length))
.filter(type => !(type in FlowVisitorKeys));
if (invalid.length > 0) {
throw new Error(`Unknown Flow node types in visitor: ${invalid.join(', ')}`);
}
} Type guard
/** True when `type` is a real Flow AST node type usable as a visitor selector. */
import {FlowVisitorKeys} from 'flow-ast';
function isFlowNodeType(type) {
return typeof type === 'string' && type in FlowVisitorKeys;
} Try / catch
try {
traverse(ast, visitor);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Unexpected selector')) {
// a visitor key names a non-Flow node type; fix the key names
console.error('bad visitor key, not a Flow node type:', err.message);
} else {
throw err;
}
} Prevention
- Run validateVisitorKeys(visitor) at startup in development builds.
- Keep a cheat-sheet of Flow type names (Literal vs StringLiteral, no TS* types) when porting from Babel/ESLint.
- Enable Flow type checking on codemod sources so misspelled visit keys surface.
When it happens
Trigger: Defining a visitor with a key for a non-Flow node type, e.g. visitTSTypeAnnotation() or visitClassPrivateProperty(), which parses to identifier selector 'TSTypeAnnotation' that is absent from FlowVisitorKeys; misspelling a real type such as visitCallExpresion(); using a selector like 'StringLiteral' (Babel naming) instead of Flow's 'Literal'.
Common situations: Porting an ESLint/Babel codemod to flow-transform and keeping TS/Babel type names; renaming refactors that break a visitor key; copy-pasting selectors from documentation for a different parser.
Related errors
- No visitor keys found for node type "${node.type}".
- 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/51c2aa16635bdfa8.
Report an issue: GitHub.