tree-sitter/tree-sitter · error · Error

`startPosition` cannot be greater than `endPosition`

Error message

`startPosition` cannot be greater than `endPosition`

What it means

Plain Error thrown by Query.matches() when startPosition and endPosition are supplied, endPosition is not the default ZERO_POINT, and startPosition is lexicographically after endPosition (greater row, or equal row with greater column). Points are {row, column} pairs compared row-first. As with the index checks, the default ZERO_POINT endPosition disables the check.

Source

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

    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`');
    }

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

    if (progressCallback) {
      C.currentQueryProgressCallback = progressCallback;
    }

View on GitHub (pinned to dff1fd868c)

Solutions

  1. Normalize the pair with a point-comparison helper before the call: if (cmpPoint(start, end) > 0) [start, end] = [end, start].
  2. Double-check the mapping when converting from editor APIs: row = line, column = character — and construct Points explicitly, never by field order.
  3. Sort selection endpoints (anchor/focus) into (start, end) before building options.
  4. Skip the query when the range is empty or inverted instead of letting the library throw.

Example fix

// before
const opts = { startPosition: sel.head, endPosition: sel.anchor }; // head can precede anchor
const ms = query.matches(node, opts);

// after
const cmp = (a, b) => a.row - b.row || a.column - b.column;
const [startPosition, endPosition] = cmp(sel.anchor, sel.head) <= 0
  ? [sel.anchor, sel.head]
  : [sel.head, sel.anchor];
const ms = query.matches(node, { startPosition, endPosition });
Defensive patterns

Strategy: validation

Validate before calling

const cmpPoint = (a: Point, b: Point) => a.row - b.row || a.column - b.column;
let { startPosition = ZERO_POINT, endPosition = ZERO_POINT } = options;
if (cmpPoint(startPosition, endPosition) > 0) {
  [startPosition, endPosition] = [endPosition, startPosition];
  options = { ...options, startPosition, endPosition };
}
const matches = query.matches(node, options);

Type guard

function isPoint(v: unknown): v is { row: number; column: number } {
  return typeof v === 'object' && v !== null
    && Number.isFinite((v as any).row) && Number.isFinite((v as any).column);
}

Try / catch

try {
  const matches = query.matches(node, options);
} catch (e) {
  if (e instanceof Error && e.message === '`startPosition` cannot be greater than `endPosition`') {
    const [a, b] = [options.startPosition, options.endPosition].sort(
      (x, y) => x!.row - y!.row || x!.column - y!.column);
    matches = query.matches(node, { ...options, startPosition: a, endPosition: b });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling query.matches(node, { startPosition: { row: 10, column: 0 }, endPosition: { row: 5, column: 20 } }); storing Points as {x, y} or {line, character} and mapping fields in the wrong order; not normalizing editor selections (anchor may be after head).

Common situations: Converting from editor coordinate types (VS Code Position {line, character}, CodeMirror {line, ch}, LSP Position) to tree-sitter Point and swapping row/column; using selection.anchor/selection.head without sorting; computing a start Point from a later marker than the end Point during incremental updates.

Related errors


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