{"record":{"id":"93c8429ae2b8d3e3","repo":"facebook/flow","slug":"syntax-error-in-selector-rawselector-at-posit","errorCode":null,"errorMessage":"Syntax error in selector \"${rawSelector}\" at position ${err.location.start.offset}: ${err.message}","messagePattern":"Syntax error in selector \"(.+?)\" at position (.+?): (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/flow-transform/src/traverse/NodeEventGenerator.js","lineNumber":235,"sourceCode":"  );\n}\n\n/**\n * Parses a raw selector string, and throws a useful error if parsing fails.\n * @param rawSelector A raw AST selector\n * @returns An object (from esquery) describing the matching behavior of this selector\n * @throws An error if the selector is invalid\n */\nfunction tryParseSelector(rawSelector: string): Selector {\n  try {\n    return esquery.parse(rawSelector.replace(/:exit$/u, ''));\n  } catch (err) {\n    if (\n      err.location &&\n      err.location.start &&\n      typeof err.location.start.offset === 'number'\n    ) {\n      throw new SyntaxError(\n        `Syntax error in selector \"${rawSelector}\" at position ${err.location.start.offset}: ${err.message}`,\n      );\n    }\n    throw err;\n  }\n}\n\nconst selectorCache = new Map<string, ParsedSelector>();\n\n/**\n * Parses a raw selector string, and returns the parsed selector along with specificity and type information.\n * @param rawSelector A raw AST selector\n * @returns A selector descriptor\n */\nfunction parseSelector(rawSelector: string): ParsedSelector {\n  const cachedSelector = selectorCache.get(rawSelector);\n  if (cachedSelector) {\n    return cachedSelector;","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/packages/flow-transform/src/traverse/NodeEventGenerator.js#L217-L253","documentation":"SyntaxError thrown by flow-transform's traverse when an esquery selector fails to parse. The raw selector (with a trailing `:exit` stripped) goes through `esquery.parse`; parse errors that carry a location are rethrown as SyntaxError with the offending selector, the character offset, and esquery's message. Thrown during listener registration, before traversal starts.","triggerScenarios":"A visitor key that is not valid esquery: unbalanced brackets (`'CallExpression >'`), a bare `':exit'` (empty after stripping), invalid pseudos (`':nth-child()'` without an argument), stray characters, or dynamically built selector strings that are malformed.","commonSituations":"Programmatically composing selectors (template strings with missing parts); typos in `:exit` (`':exi'`); using CSS features esquery does not support; quotes lost when selectors pass through JSON configs.","solutions":["Validate every visitor key with `esquery.parse` at startup or in unit tests before wiring traverse.","Remember `:exit` is the only supported suffix and it must be last (it is stripped before parsing).","Check esquery's selector grammar (identifiers, `>`, ` `, `,`, attribute brackets, `:not`, `:matches`, `:nth-child(n)`).","Log the raw selector on failure - the reported offset indexes into the pre-`:exit` string."],"exampleFix":"// before\ntraverseWithContext(code, ast, scope, () => ({}), ctx => ({\n  'FunctionDeclaration >': node => {}, // dangling combinator - SyntaxError\n}));\n\n// after\ntraverseWithContext(code, ast, scope, () => ({}), ctx => ({\n  'FunctionDeclaration > Identifier': node => {},\n}));","handlingStrategy":"validation","validationCode":"import esquery from 'esquery';\n\nfunction isValidSelector(rawSelector: string): boolean {\n  try {\n    esquery.parse(rawSelector.replace(/:exit$/u, ''));\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nconst bad = Object.keys(selectors).filter(s => !isValidSelector(s));\nif (bad.length > 0) throw new Error(`invalid selectors: ${bad.join(', ')}`);","typeGuard":null,"tryCatchPattern":"try {\n  traverseWithContext(code, ast, scope, () => ({}), visitors);\n} catch (e) {\n  if (e instanceof SyntaxError && /selector/.test(e.message)) {\n    // a visitor key is malformed - the message includes the offset into the selector\n  }\n  throw e;\n}","preventionTips":["Unit-test every visitor key with esquery.parse before shipping codemods.","Build dynamic selectors from validated fragments; never interpolate unvalidated input.","Remember ':exit' is stripped before parsing and must be the trailing suffix."],"tags":["flow","flow-transform","esquery","selector","syntax-error","visitor"],"backgroundTag":"invalid-ast-selector","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","contentChangedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}