{"record":{"id":"267c06ed5204f581","repo":"tree-sitter/tree-sitter","slug":"arguments-must-be-numbers-267c06","errorCode":null,"errorMessage":"Arguments must be numbers","messagePattern":"Arguments must be numbers","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/binding_web/src/query.ts","lineNumber":734,"sourceCode":"   */\n  matches(\n    node: Node,\n    options: QueryOptions = {}\n  ): QueryMatch[] {\n    const startPosition = options.startPosition ?? ZERO_POINT;\n    const endPosition = options.endPosition ?? ZERO_POINT;\n    const startIndex = options.startIndex ?? 0;\n    const endIndex = options.endIndex ?? 0;\n    const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;\n    const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;\n    const startContainingIndex = options.startContainingIndex ?? 0;\n    const endContainingIndex = options.endContainingIndex ?? 0;\n    const matchLimit = options.matchLimit ?? 0xFFFFFFFF;\n    const maxStartDepth = options.maxStartDepth ?? 0xFFFFFFFF;\n    const progressCallback = options.progressCallback;\n\n    if (typeof matchLimit !== 'number') {\n      throw new Error('Arguments must be numbers');\n    }\n    this.matchLimit = matchLimit;\n\n    if (endIndex !== 0 && startIndex > endIndex) {\n      throw new Error('`startIndex` cannot be greater than `endIndex`');\n    }\n\n    if (endPosition !== ZERO_POINT && (\n      startPosition.row > endPosition.row ||\n      (startPosition.row === endPosition.row && startPosition.column > endPosition.column)\n    )) {\n      throw new Error('`startPosition` cannot be greater than `endPosition`');\n    }\n\n    if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {\n      throw new Error('`startContainingIndex` cannot be greater than `endContainingIndex`');\n    }\n","sourceCodeStart":716,"sourceCodeEnd":752,"githubUrl":"https://github.com/tree-sitter/tree-sitter/blob/dff1fd868c750dbbae179fcd5c43ce987e4e0528/lib/binding_web/src/query.ts#L716-L752","documentation":"Plain Error thrown by Query.matches() in the web binding. After destructuring the options object, the only argument type actually enforced is matchLimit: if options.matchLimit was supplied and typeof matchLimit !== 'number', it throws 'Arguments must be numbers' before touching the wasm cursor. Note the misleading breadth of the message — only matchLimit is checked here, and NaN passes because typeof NaN === 'number'.","triggerScenarios":"Calling query.matches(node, { matchLimit: '1000' }) with a string; matchLimit coming from URLSearchParams, localStorage, process.env, or a JSON config file (all yield strings); passing a BigInt (typeof 'bigint'); passing an options object typed as any in JavaScript so the mistake is not caught at compile time.","commonSituations":"Reading matchLimit/maxStartDepth from user configuration (editor settings, query URLs) without coercing to Number; JS projects without TypeScript checking where a string slips in; migrating code from the node binding (which validates differently) to the web binding.","solutions":["Coerce the value before the call: matchLimit: Number(options.matchLimit) — or fix the source so a real number is passed.","If the value comes from env/config, parse once at load time (parseInt/Number) and validate with Number.isFinite.","Enable TypeScript and type the options argument as QueryOptions so string/BigInt mismatches are compile-time errors.","Remember NaN is not caught by this check — guard Number.isFinite(matchLimit) yourself."],"exampleFix":"// before\nconst limit = params.get('matchLimit'); // \"5000\" (string)\nconst matches = query.matches(node, { matchLimit: limit }); // throws\n\n// after\nconst limit = Number(params.get('matchLimit'));\nconst matches = query.matches(node, { matchLimit: Number.isFinite(limit) ? limit : undefined });","handlingStrategy":"type-guard","validationCode":"const limit = options.matchLimit;\nif (limit !== undefined && (typeof limit !== 'number' || !Number.isFinite(limit))) {\n  throw new TypeError(`matchLimit must be a finite number, got ${typeof limit}`);\n}\nconst matches = query.matches(node, options);","typeGuard":"function isFiniteNumber(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v);\n}\n\n// usage\nif (isFiniteNumber(opts.matchLimit)) query.matches(node, opts);\nelse query.matches(node, { ...opts, matchLimit: Number(opts.matchLimit) || undefined });","tryCatchPattern":"try {\n  const matches = query.matches(node, options);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Arguments must be numbers') {\n    options = { ...options, matchLimit: Number(options.matchLimit) };\n    matches = query.matches(node, options); // retry with coerced value\n  } else throw e;\n}","preventionTips":["Coerce config/env/URL-sourced numbers once at load time with Number() and reject non-finite results.","Type the options argument as QueryOptions so TypeScript catches strings at compile time.","Remember the runtime check accepts NaN — validate Number.isFinite yourself.","Don't ship raw strings into query options via spread from JSON payloads."],"tags":["tree-sitter","web-binding","argument-validation","type-mismatch"],"backgroundTag":"invalid-argument-type","analyzedSha":"dff1fd868c750dbbae179fcd5c43ce987e4e0528","analyzedAt":"2026-08-16T22:01:49.632Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}