{"record":{"id":"877de9aeba5880f2","repo":"tree-sitter/tree-sitter","slug":"pattern-index-is-patternindex-but-the-pattern-c","errorCode":null,"errorMessage":"Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}","messagePattern":"Pattern index is (.+?) but the pattern count is (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/binding_web/src/query.ts","lineNumber":965,"sourceCode":"   */\n  disableCapture(captureName: string): void {\n    const captureNameLength = C.lengthBytesUTF8(captureName);\n    const captureNameAddress = C._malloc(captureNameLength + 1);\n    C.stringToUTF8(captureName, captureNameAddress, captureNameLength + 1);\n    C._ts_query_disable_capture(this[0], captureNameAddress, captureNameLength);\n    C._free(captureNameAddress);\n  }\n\n  /**\n   * Disable a certain pattern within a query.\n   *\n   * This prevents the pattern from matching, and also avoids any resource\n   * usage associated with the pattern. This throws an error if the pattern\n   * index is out of bounds.\n   */\n  disablePattern(patternIndex: number): void {\n    if (patternIndex >= this.predicates.length) {\n      throw new Error(\n        `Pattern index is ${patternIndex} but the pattern count is ${this.predicates.length}`\n      );\n    }\n    C._ts_query_disable_pattern(this[0], patternIndex);\n  }\n\n  /**\n   * Check if, on its last execution, this cursor exceeded its maximum number\n   * of in-progress matches.\n   */\n  didExceedMatchLimit(): boolean {\n    return this.exceededMatchLimit;\n  }\n\n  /** Get the byte offset where the given pattern starts in the query's source. */\n  startIndexForPattern(patternIndex: number): number {\n    if (patternIndex >= this.predicates.length) {\n      throw new Error(","sourceCodeStart":947,"sourceCodeEnd":983,"githubUrl":"https://github.com/tree-sitter/tree-sitter/blob/dff1fd868c750dbbae179fcd5c43ce987e4e0528/lib/binding_web/src/query.ts#L947-L983","documentation":"Plain Error thrown by Query.disablePattern(patternIndex) when patternIndex >= this.predicates.length (predicates is built per pattern in the constructor, so its length equals the query's pattern count). The guard bounds-checks the index before calling _ts_query_disable_pattern on the wasm side. Note the check only catches too-large indices — a negative patternIndex passes this guard.","triggerScenarios":"Calling disablePattern(query.patternCount()) or higher (classic <= vs < loop bound); hard-coding an index (e.g. disablePattern(3)) and later editing the query source so it has fewer patterns; using a pattern index obtained from a different Query object; computing the bound from a stale patternCount captured before the query was rebuilt.","commonSituations":"Dynamic rule toggles in editor plugins (disable this highlight pattern) where the .scm file evolves between releases; loops written as for (let i = 0; i <= query.patternCount(); i++); mixing indices from match results of one query with a second, smaller query.","solutions":["Check the bound first: if (patternIndex < query.patternCount()) query.disablePattern(patternIndex);","Fix off-by-one loops to use i < query.patternCount(), not <=.","Stop hard-coding pattern indices: look patterns up by behavior (predicatesForPattern) or re-derive indices after every change to the query source.","Always call patternCount() on the same Query instance you are disabling on, at call time."],"exampleFix":"// before\nfor (let i = 0; i <= query.patternCount(); i++) query.disablePattern(i); // last i throws\n\n// after\nfor (let i = 0; i < query.patternCount(); i++) query.disablePattern(i);","handlingStrategy":"validation","validationCode":"const count = query.patternCount();\nif (!Number.isInteger(patternIndex) || patternIndex < 0 || patternIndex >= count) {\n  throw new RangeError(`patternIndex must be in [0, ${count})`);\n}\nquery.disablePattern(patternIndex);","typeGuard":"function isValidPatternIndex(query: Query, i: unknown): i is number {\n  return Number.isInteger(i) && (i as number) >= 0 && (i as number) < query.patternCount();\n}","tryCatchPattern":"try {\n  query.disablePattern(patternIndex);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Pattern index is')) {\n    console.warn(`Skipping disable: pattern ${patternIndex} does not exist (count ${query.patternCount()})`);\n  } else throw e;\n}","preventionTips":["Bound-check against query.patternCount() at call time, on the same Query instance.","Use strict < in loops over pattern indices.","Never hard-code pattern indices; re-derive them after editing the query source.","Remember the library check misses negative indices — validate >= 0 yourself."],"tags":["tree-sitter","web-binding","index-out-of-bounds","query"],"backgroundTag":"index-out-of-bounds","analyzedSha":"dff1fd868c750dbbae179fcd5c43ce987e4e0528","analyzedAt":"2026-08-16T22:01:49.632Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}