{"record":{"id":"e8536579a23d9dad","repo":"neoclide/coc.nvim","slug":"illegal-value-for-line","errorCode":null,"errorMessage":"Illegal value for `line`","messagePattern":"Illegal value for `line`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/model/textdocument.ts","lineNumber":107,"sourceCode":"    } else if (range.end.line === end.line) {\n      end = Position.create(end.line, Math.min(end.character, range.end.character))\n    }\n\n    return Range.create(start, end)\n  }\n\n  public getText(range?: Range): string {\n    if (range) return getRangeText(this.lines, range)\n    return this.content\n  }\n\n  public lineAt(lineOrPos: number | Position): TextLine {\n    const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos\n    if (typeof line !== 'number' ||\n      line < 0 ||\n      line >= this.lineCount ||\n      Math.floor(line) !== line) {\n      throw new Error('Illegal value for `line`')\n    }\n\n    return new TextLine(line, this.lines[line] ?? '', line === this.lineCount - 1)\n  }\n\n  public positionAt(offset: number): Position {\n    offset = Math.max(Math.min(offset, this.content.length), 0)\n    let lineOffsets = this.getLineOffsets()\n    let low = 0\n    let high = lineOffsets.length\n    if (high === 0) {\n      return { line: 0, character: offset }\n    }\n    while (low < high) {\n      let mid = Math.floor((low + high) / 2)\n      if (lineOffsets[mid] > offset) {\n        high = mid\n      } else {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/model/textdocument.ts#L89-L125","documentation":"TextDocument.lineAt() validates that the requested line is a non-negative integer within the document (0 <= line < lineCount) before constructing TextLine. Any out-of-bounds, non-integer, or non-number value throws this error.","triggerScenarios":"Calling doc.lineAt(-1), doc.lineAt(doc.lineCount), doc.lineAt(1.5), or passing a Position whose line is past the end of the (possibly shrunken) document — often because the doc snapshot is stale relative to a Position.","commonSituations":"Iterating lines with an off-by-one (line <= lineCount); using a Position captured before document edits/deletions; empty documents where lineCount is 1 and code assumes 0 lines; floating point line values from arithmetic.","solutions":["Clamp: doc.lineAt(Math.min(Math.max(0, Math.floor(line)), doc.lineCount - 1))","Check bounds first: if (line >= 0 && line < doc.lineCount) doc.lineAt(line)","Re-fetch positions/doc after edits instead of reusing stale Positions","Use doc.positionAt(offset) for offset-based access instead of manual line math"],"exampleFix":"// before\nconst last = doc.lineAt(doc.lineCount) // off by one\n// after\nconst last = doc.lineAt(doc.lineCount - 1)","handlingStrategy":"validation","validationCode":"function canLineAt(doc: TextDocument, line: number): boolean {\n  return typeof line === 'number' && line >= 0 && line < doc.lineCount && Number.isInteger(line)\n}","typeGuard":"function isValidLine(doc: TextDocument, p: number | Position): boolean {\n  const line = Position.is(p) ? p.line : p\n  return typeof line === 'number' && Number.isInteger(line) && line >= 0 && line < doc.lineCount\n}","tryCatchPattern":"try {\n  const textLine = doc.lineAt(line)\n} catch (e) {\n  if (e.message.includes('Illegal value for `line`')) {\n    const clamped = Math.min(Math.max(0, Math.floor(line)), doc.lineCount - 1)\n    return doc.lineAt(clamped)\n  }\n}","preventionTips":["Use lineCount - 1 for the last line, not lineCount","Re-derive Positions from the current document snapshot after edits","Prefer doc.positionAt(offset) over manual line arithmetic","Round/clamp any computed line numbers before access"],"tags":["range","bounds-check","textdocument"],"backgroundTag":"index-out-of-range","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}