microsoft/typescript-go · error
Bad line number. Line: ${line}, lineStarts.length: ${lineSta
Error message
Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} What it means
Thrown by RemoteSourceFile.getPositionOfLineAndCharacter when the requested line is negative or >= lineStarts.length (the number of lines in the file's text). Lines are strictly 0-based and computed from the snapshot's text; the guard rejects any line index outside the file, including the classic 1-based off-by-one.
Source
Thrown at _packages/native-preview/src/api/node/node.ts:261
return text;
}
// ═══ Line/character position mapping ═══
getLineStarts(): readonly number[] {
return this._lineStarts ??= computeLineStarts(this.text ?? "");
}
getLineAndCharacterOfPosition(position: number): LineAndCharacter {
const lineStarts = this.getLineStarts();
const line = computeLineOfPosition(lineStarts, position);
return { line, character: position - lineStarts[line] };
}
getPositionOfLineAndCharacter(line: number, character: number): number {
const lineStarts = this.getLineStarts();
if (line < 0 || line >= lineStarts.length) {
throw new Error(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length}`);
}
return lineStarts[line] + character;
}
}
/**
* Find the 0-based line number containing the given position via binary search.
* Assumes the first line starts at position 0 and `position` is non-negative.
*/
function computeLineOfPosition(lineStarts: readonly number[], position: number): number {
let low = 0;
let high = lineStarts.length - 1;
while (low <= high) {
const middle = low + ((high - low) >> 1);
const value = lineStarts[middle];
if (value < position) {
low = middle + 1;
}
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Convert external 1-based lines to 0-based before calling (subtract 1)
- Validate against the file itself: line >= 0 && line < sourceFile.getLineStarts().length before the call
- Recompute positions from the same text snapshot you query (avoid mixing versions)
- Clamp instead of throwing when the position is merely past-the-end by design
Example fix
// before const pos = sf.getPositionOfLineAndCharacter(lineFromDiagnostics, col); // 1-based -> throws // after const line = Math.max(0, Math.min(lineFromDiagnostics - 1, sf.getLineStarts().length - 1)); const pos = sf.getPositionOfLineAndCharacter(line, col);
Defensive patterns
Strategy: validation
Validate before calling
// Validate/clamp the line against this exact file before converting
const lineCount = sf.getLineStarts().length;
if (line < 0) throw new RangeError('negative line');
const safeLine = Math.min(line, lineCount - 1); // clamp instead of throwing when past-end is benign
const pos = sf.getPositionOfLineAndCharacter(safeLine, character); Type guard
const isValidLine = (sf: { getLineStarts(): readonly number[] }, line: number): boolean =>
Number.isInteger(line) && line >= 0 && line < sf.getLineStarts().length; Try / catch
try {
offset = sf.getPositionOfLineAndCharacter(line, character);
} catch (e) {
if (e instanceof Error && e.message.includes('Bad line number')) {
// stale or 1-based input: recompute from a fresh snapshot, or clamp
offset = sf.positionAt(sf.getLineStarts()[sf.getLineStarts().length - 1]);
} else throw e;
} Prevention
- Convert every external (1-based) line to 0-based at your system boundary
- Derive line/character from the same text snapshot you convert against
- Prefer getLineAndCharacterOfPosition round-trips over hand-built indices
When it happens
Trigger: Passing a 1-based line from user input, an editor, or a diagnostic payload; using a line computed against a newer/older version of the file than the text backing this RemoteSourceFile; line === lineStarts.length (one past the last line, common when appending); empty/synthetic files where lineStarts has a single entry.
Common situations: Converting IDE positions (often 1-based) or compiler API line numbers to offsets; stale buffers after edits; scripts iterating to <= line count; feeds from logs/tsserver-style data that are 1-based.
Related errors
- Cannot run a temporary file update on an inactive snapshot
- Snapshot is disposed
- Cannot create directory: a file already exists at "/${segmen
- Invalid file path: "${path}"
- Expected ${pkgName} to declare exactly one bin entry named $
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/a7dec913abd65544.
Report an issue: GitHub.