{"record":{"id":"a7dec913abd65544","repo":"microsoft/typescript-go","slug":"bad-line-number-line-line-linestarts-length","errorCode":null,"errorMessage":"Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length}","messagePattern":"Bad line number\\. Line: (.+?), lineStarts\\.length: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"_packages/native-preview/src/api/node/node.ts","lineNumber":261,"sourceCode":"        return text;\r\n    }\r\n\r\n    // ═══ Line/character position mapping ═══\r\n\r\n    getLineStarts(): readonly number[] {\r\n        return this._lineStarts ??= computeLineStarts(this.text ?? \"\");\r\n    }\r\n\r\n    getLineAndCharacterOfPosition(position: number): LineAndCharacter {\r\n        const lineStarts = this.getLineStarts();\r\n        const line = computeLineOfPosition(lineStarts, position);\r\n        return { line, character: position - lineStarts[line] };\r\n    }\r\n\r\n    getPositionOfLineAndCharacter(line: number, character: number): number {\r\n        const lineStarts = this.getLineStarts();\r\n        if (line < 0 || line >= lineStarts.length) {\r\n            throw new Error(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length}`);\r\n        }\r\n        return lineStarts[line] + character;\r\n    }\r\n}\r\n\r\n/**\r\n * Find the 0-based line number containing the given position via binary search.\r\n * Assumes the first line starts at position 0 and `position` is non-negative.\r\n */\r\nfunction computeLineOfPosition(lineStarts: readonly number[], position: number): number {\r\n    let low = 0;\r\n    let high = lineStarts.length - 1;\r\n    while (low <= high) {\r\n        const middle = low + ((high - low) >> 1);\r\n        const value = lineStarts[middle];\r\n        if (value < position) {\r\n            low = middle + 1;\r\n        }\r","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/_packages/native-preview/src/api/node/node.ts#L243-L279","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst pos = sf.getPositionOfLineAndCharacter(lineFromDiagnostics, col); // 1-based -> throws\n\n// after\nconst line = Math.max(0, Math.min(lineFromDiagnostics - 1, sf.getLineStarts().length - 1));\nconst pos = sf.getPositionOfLineAndCharacter(line, col);","handlingStrategy":"validation","validationCode":"// Validate/clamp the line against this exact file before converting\nconst lineCount = sf.getLineStarts().length;\nif (line < 0) throw new RangeError('negative line');\nconst safeLine = Math.min(line, lineCount - 1); // clamp instead of throwing when past-end is benign\nconst pos = sf.getPositionOfLineAndCharacter(safeLine, character);","typeGuard":"const isValidLine = (sf: { getLineStarts(): readonly number[] }, line: number): boolean =>\n    Number.isInteger(line) && line >= 0 && line < sf.getLineStarts().length;","tryCatchPattern":"try {\n    offset = sf.getPositionOfLineAndCharacter(line, character);\n} catch (e) {\n    if (e instanceof Error && e.message.includes('Bad line number')) {\n        // stale or 1-based input: recompute from a fresh snapshot, or clamp\n        offset = sf.positionAt(sf.getLineStarts()[sf.getLineStarts().length - 1]);\n    } else throw e;\n}","preventionTips":["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"],"tags":["api","positions","off-by-one","validation","native-preview"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}