tree-sitter/tree-sitter · error · Error

Arguments must be numbers

Error message

Arguments must be numbers

What it means

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'.

Source

Thrown at lib/binding_web/src/query.ts:734

   */
  matches(
    node: Node,
    options: QueryOptions = {}
  ): QueryMatch[] {
    const startPosition = options.startPosition ?? ZERO_POINT;
    const endPosition = options.endPosition ?? ZERO_POINT;
    const startIndex = options.startIndex ?? 0;
    const endIndex = options.endIndex ?? 0;
    const startContainingPosition = options.startContainingPosition ?? ZERO_POINT;
    const endContainingPosition = options.endContainingPosition ?? ZERO_POINT;
    const startContainingIndex = options.startContainingIndex ?? 0;
    const endContainingIndex = options.endContainingIndex ?? 0;
    const matchLimit = options.matchLimit ?? 0xFFFFFFFF;
    const maxStartDepth = options.maxStartDepth ?? 0xFFFFFFFF;
    const progressCallback = options.progressCallback;

    if (typeof matchLimit !== 'number') {
      throw new Error('Arguments must be numbers');
    }
    this.matchLimit = matchLimit;

    if (endIndex !== 0 && startIndex > endIndex) {
      throw new Error('`startIndex` cannot be greater than `endIndex`');
    }

    if (endPosition !== ZERO_POINT && (
      startPosition.row > endPosition.row ||
      (startPosition.row === endPosition.row && startPosition.column > endPosition.column)
    )) {
      throw new Error('`startPosition` cannot be greater than `endPosition`');
    }

    if (endContainingIndex !== 0 && startContainingIndex > endContainingIndex) {
      throw new Error('`startContainingIndex` cannot be greater than `endContainingIndex`');
    }

View on GitHub (pinned to dff1fd868c)

Solutions

  1. Coerce the value before the call: matchLimit: Number(options.matchLimit) — or fix the source so a real number is passed.
  2. If the value comes from env/config, parse once at load time (parseInt/Number) and validate with Number.isFinite.
  3. Enable TypeScript and type the options argument as QueryOptions so string/BigInt mismatches are compile-time errors.
  4. Remember NaN is not caught by this check — guard Number.isFinite(matchLimit) yourself.

Example fix

// before
const limit = params.get('matchLimit'); // "5000" (string)
const matches = query.matches(node, { matchLimit: limit }); // throws

// after
const limit = Number(params.get('matchLimit'));
const matches = query.matches(node, { matchLimit: Number.isFinite(limit) ? limit : undefined });
Defensive patterns

Strategy: type-guard

Validate before calling

const limit = options.matchLimit;
if (limit !== undefined && (typeof limit !== 'number' || !Number.isFinite(limit))) {
  throw new TypeError(`matchLimit must be a finite number, got ${typeof limit}`);
}
const matches = query.matches(node, options);

Type guard

function isFiniteNumber(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v);
}

// usage
if (isFiniteNumber(opts.matchLimit)) query.matches(node, opts);
else query.matches(node, { ...opts, matchLimit: Number(opts.matchLimit) || undefined });

Try / catch

try {
  const matches = query.matches(node, options);
} catch (e) {
  if (e instanceof Error && e.message === 'Arguments must be numbers') {
    options = { ...options, matchLimit: Number(options.matchLimit) };
    matches = query.matches(node, options); // retry with coerced value
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tree-sitter/tree-sitter@dff1fd868c (2026-08-16). Data as JSON: /api/errors/267c06ed5204f581. Report an issue: GitHub.