{"record":{"id":"2920fa88c755a6bb","repo":"denoland/deno","slug":"expected-token-token-but-got-this-token","errorCode":null,"errorMessage":"Expected token '${token}', but got '${this.token}'.\\n\\n${this.input}\\n${\" \".repeat(this.i)}^","messagePattern":"Expected token '(.+?)', but got '(.+?)'\\.\\\\n\\\\n(.+?)\\\\n(.+?)\\^","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/js/40_lint_selector.js","lineNumber":140,"sourceCode":"  i = -1;\n\n  value = \"\";\n\n  /**\n   * @param {string} input\n   */\n  constructor(input) {\n    this.input = input;\n    this.step();\n    this.next();\n  }\n\n  /**\n   * @param {number} token\n   */\n  expect(token) {\n    if (this.token !== token) {\n      throw new Error(\n        `Expected token '${token}', but got '${this.token}'.\\n\\n${this.input}\\n${\n          \" \".repeat(this.i)\n        }^`,\n      );\n    }\n  }\n\n  /**\n   * @param {number} token\n   */\n  readAsWordUntil(token) {\n    const s = this.i;\n    while (this.token !== Token.EOF && this.token !== token) {\n      this.next();\n    }\n\n    this.start = s;\n    this.end = this.i - 1;","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/js/40_lint_selector.js#L122-L158","documentation":"Thrown by Lexer.expect() in cli/js/40_lint_selector.js when the selector grammar requires a specific token but a different one (printed as its numeric token id) is next. The message echoes the full input and a caret pointing at the lexer position, so you can see exactly where parsing derailed.","triggerScenarios":"Malformed visitor-key selectors: unbalanced or missing brackets/parens such as \":has(CallExpression\", \"ObjectExpression[\", \"VariableDeclarator > \"; missing closing brace in \":nth-child(2\"; stray characters where a word or bracket was expected.","commonSituations":"Hand-writing long compound selectors in a rule's create() return object; a visitor key with a typo after refactoring; dynamically concatenating selector fragments and dropping a closing bracket.","solutions":["Read the caret line in the message — it marks the exact character where the expected token was not found","Balance the selector: every '[' has ']' and every '(' has ')'","Split overly long selectors into multiple visitor keys or move conditions into the callback"],"exampleFix":"// before\n\"MemberExpression[property.name === \\\"length\\\"]\"(node) {},\n\n// after\n\"MemberExpression[property.name=\\\"length\\\"]\"(node) {},","handlingStrategy":"try-catch","validationCode":"// cheap bracket-balance check for hand-built selector strings\nfunction bracketsBalanced(sel) {\n  const pairs = { \"[\": \"]\", \"(\": \")\" };\n  const stack = [];\n  for (const ch of sel) {\n    if (pairs[ch]) stack.push(pairs[ch]);\n    else if (Object.values(pairs).includes(ch) && stack.pop() !== ch) return false;\n  }\n  return stack.length === 0;\n}","typeGuard":null,"tryCatchPattern":"try {\n  lintFixtureWithPlugin(plugin); // run deno lint on a scratch file\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Expected token\")) {\n    // the message contains input + caret; fix the selector at the caret position\n    console.error(err.message);\n  }\n  throw err;\n}","preventionTips":["Keep visitor keys short and literal — avoid concatenating selector fragments","When composing selectors from parts, validate bracket balance before use"],"tags":["lint","selector","esquery","parser","syntax"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}