facebook/flow · error · SyntaxError

Syntax error in selector "${rawSelector}" at position ${err.

Error message

Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}

What it means

tryParseSelector() runs each selector string through esquery.parse() (after stripping a trailing :exit) and rethrows parse failures as a SyntaxError that includes the offset and the underlying esquery message. Selectors originate from the event names registered on your visitor — keys that are not visitXxx method names are treated as raw esquery strings. This error means one of those strings is not valid esquery syntax, for example an unbalanced bracket, a stray combinator, or a malformed pseudo-class.

Source

Thrown at packages/flow-transform/src/traverse/NodeEventGenerator.js:235

  );
}

/**
 * Parses a raw selector string, and throws a useful error if parsing fails.
 * @param rawSelector A raw AST selector
 * @returns An object (from esquery) describing the matching behavior of this selector
 * @throws An error if the selector is invalid
 */
function tryParseSelector(rawSelector: string): Selector {
  try {
    return esquery.parse(rawSelector.replace(/:exit$/u, ''));
  } catch (err) {
    if (
      err.location &&
      err.location.start &&
      typeof err.location.start.offset === 'number'
    ) {
      throw new SyntaxError(
        `Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`,
      );
    }
    throw err;
  }
}

const selectorCache = new Map<string, ParsedSelector>();

/**
 * Parses a raw selector string, and returns the parsed selector along with specificity and type information.
 * @param rawSelector A raw AST selector
 * @returns A selector descriptor
 */
function parseSelector(rawSelector: string): ParsedSelector {
  const cachedSelector = selectorCache.get(rawSelector);
  if (cachedSelector) {
    return cachedSelector;

View on GitHub (pinned to d1341dac89)

Solutions

  1. Fix the selector syntax per esquery grammar (balanced brackets, valid pseudo-classes, supported combinators); keep ':exit' as the exact suffix.
  2. Dry-run validation: call esquery.parse on each selector string during development/startup so failures surface with position before traversal.
  3. Prefix non-selector helper properties so they are not collected as event names, or keep them off the visitor object.
  4. Use simple type-name selectors (visitXxx) unless you need esquery complexity.

Example fix

// before
const visitor = {
  visitCallExpression(node) {},
  'CallExpression > ': fn, // trailing combinator -> SyntaxError
};

// after
const visitor = {
  visitCallExpression(node) {},
  'CallExpression > MemberExpression': fn, // valid esquery
};
Defensive patterns

Strategy: validation

Validate before calling

import esquery from 'esquery';

function validateSelectors(selectors) {
  for (const raw of selectors) {
    try {
      esquery.parse(raw.replace(/:exit$/u, ''));
    } catch {
      throw new Error(`Invalid AST selector: ${JSON.stringify(raw)}`);
    }
  }
}

Try / catch

try {
  traverse(ast, visitor);
} catch (err) {
  if (err instanceof SyntaxError && err.message.includes('Syntax error in selector')) {
    // the message names the raw selector and offset; fix or drop that selector
    console.error(err.message);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Adding an arbitrary string key to the visitor object such as 'CallExpression >' , '[async=true' , or ':exitt' (misspelled exit pseudo); registering selector strings with unsupported esquery features; trailing whitespace or smart quotes pasted from docs.

Common situations: Using advanced esquery attribute/pseudo selectors and getting the syntax slightly wrong; typos in the ':exit' suffix; keys on the visitor object that were meant as helper methods but got parsed as selectors because they do not start with 'visit'.

Related errors


AI-assisted analysis of facebook/flow@d1341dac89 (2026-08-17). Data as JSON: /api/errors/93c8429ae2b8d3e3. Report an issue: GitHub.