neoclide/coc.nvim · error

Illegal value for `line`

Error message

Illegal value for `line`

What it means

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.

Source

Thrown at src/model/textdocument.ts:107

    } else if (range.end.line === end.line) {
      end = Position.create(end.line, Math.min(end.character, range.end.character))
    }

    return Range.create(start, end)
  }

  public getText(range?: Range): string {
    if (range) return getRangeText(this.lines, range)
    return this.content
  }

  public lineAt(lineOrPos: number | Position): TextLine {
    const line = Position.is(lineOrPos) ? lineOrPos.line : lineOrPos
    if (typeof line !== 'number' ||
      line < 0 ||
      line >= this.lineCount ||
      Math.floor(line) !== line) {
      throw new Error('Illegal value for `line`')
    }

    return new TextLine(line, this.lines[line] ?? '', line === this.lineCount - 1)
  }

  public positionAt(offset: number): Position {
    offset = Math.max(Math.min(offset, this.content.length), 0)
    let lineOffsets = this.getLineOffsets()
    let low = 0
    let high = lineOffsets.length
    if (high === 0) {
      return { line: 0, character: offset }
    }
    while (low < high) {
      let mid = Math.floor((low + high) / 2)
      if (lineOffsets[mid] > offset) {
        high = mid
      } else {

View on GitHub (pinned to 50e974d969)

Solutions

  1. Clamp: doc.lineAt(Math.min(Math.max(0, Math.floor(line)), doc.lineCount - 1))
  2. Check bounds first: if (line >= 0 && line < doc.lineCount) doc.lineAt(line)
  3. Re-fetch positions/doc after edits instead of reusing stale Positions
  4. Use doc.positionAt(offset) for offset-based access instead of manual line math

Example fix

// before
const last = doc.lineAt(doc.lineCount) // off by one
// after
const last = doc.lineAt(doc.lineCount - 1)
Defensive patterns

Strategy: validation

Validate before calling

function canLineAt(doc: TextDocument, line: number): boolean {
  return typeof line === 'number' && line >= 0 && line < doc.lineCount && Number.isInteger(line)
}

Type guard

function isValidLine(doc: TextDocument, p: number | Position): boolean {
  const line = Position.is(p) ? p.line : p
  return typeof line === 'number' && Number.isInteger(line) && line >= 0 && line < doc.lineCount
}

Try / catch

try {
  const textLine = doc.lineAt(line)
} catch (e) {
  if (e.message.includes('Illegal value for `line`')) {
    const clamped = Math.min(Math.max(0, Math.floor(line)), doc.lineCount - 1)
    return doc.lineAt(clamped)
  }
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/e8536579a23d9dad. Report an issue: GitHub.