{"record":{"id":"4f70498b9679234f","repo":"chatboxai/chatbox","slug":"offset-startline-is-beyond-end-of-file-total","errorCode":null,"errorMessage":"Offset ${startLine} is beyond end of file (${totalLines} lines total)","messagePattern":"Offset (.+?) is beyond end of file \\((.+?) lines total\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/sandbox/file-read.ts","lineNumber":53,"sourceCode":";(async () => {\n  for await (const line of lines) {\n    totalLines++\n    if (totalLines >= startLine && selected.length < limit && !selectionFull) {\n      const candidate = line.length > maxLineLength ? line.slice(0, maxLineLength - 3) + '...' : line\n      // JSON.stringify includes the surrounding quotes. Subtract those two bytes, then account\n      // for the escaped newline separator that selected.join('\\\\n') adds between lines.\n      const candidateBytes = Buffer.byteLength(JSON.stringify(candidate), 'utf8') - 2\n      const separatorBytes = selected.length > 0 ? 2 : 0\n      if (selectedBytes + separatorBytes + candidateBytes > maxContentBytes) {\n        selectionFull = true\n      } else {\n        selected.push(candidate)\n        selectedBytes += separatorBytes + candidateBytes\n      }\n    }\n  }\n  if (startLine > totalLines && !(totalLines === 0 && startLine === 1)) {\n    throw new Error('Offset ' + startLine + ' is beyond end of file (' + totalLines + ' lines total)')\n  }\n  const endLine = selected.length > 0 ? startLine + selected.length - 1 : 0\n  process.stdout.write(JSON.stringify({ content: selected.join('\\\\n'), startLine, endLine, totalLines }))\n})().catch((error) => {\n  console.error(error instanceof Error ? error.message : String(error))\n  process.exitCode = 1\n})\n`\n}\n","sourceCodeStart":35,"sourceCodeEnd":63,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/sandbox/file-read.ts#L35-L63","documentation":"Thrown by the sandboxed Node read program emitted by buildSandboxReadScript. After streaming the entire file to count totalLines, if startLine exceeds totalLines (and the file is not empty with startLine===1, the 'read empty file from line 1' special case) the program throws before writing its JSON line. The exit path prints error.message to stderr and sets process.exitCode=1.","triggerScenarios":"Caller passes startLine greater than the file's actual line count (e.g. requesting offset 500 from a 50-line file), or the file was truncated/replaced with shorter content between when the caller fetched totalLines and when the read script runs. Also when startLine was computed from a stale totalLines cached elsewhere.","commonSituations":"Pagination UI held an old totalLines and the file shrank (log rotation, overwrite); a tool computed startLine = lastEndLine + 1 without clamping to totalLines; reading a file that is actively being written and got rotated; off-by-one where startLine=totalLines+1.","solutions":["Before invoking the reader, clamp startLine to Math.min(startLine, lastKnownTotalLines) and treat startLine > totalLines as 'no more content' rather than an error.","On this error, treat totalLines as authoritative (re-read with startLine=1 or surface 'file changed, reload').","If reading volatile files, capture a fresh totalLines immediately before paginating instead of trusting a cached value.","Guard the empty-file edge case: if you expect possibly-empty files, pass startLine=1 (the special case is handled)."],"exampleFix":"// before\nconst startLine = requestedOffset // can exceed file length\n\n// after\nconst startLine = Math.max(1, Math.min(requestedOffset, lastKnownTotalLines || 1))","handlingStrategy":"validation","validationCode":"const startLine = requestedOffset\nif (lastKnownTotalLines && startLine > lastKnownTotalLines) { return { content: '', startLine, endLine: 0, totalLines: lastKnownTotalLines } }","typeGuard":"function isOffsetBeyondEof(e: unknown): e is Error {\n  return e instanceof Error && /^Offset \\d+ is beyond end of file/.test(e.message)\n}","tryCatchPattern":"try { return await readSandboxFile(path, requestedOffset, limit) }\ncatch (e) { if (isOffsetBeyondEof(e)) { return { content: '', startLine: requestedOffset, endLine: 0, totalLines: 0 /* re-fetch */ } } throw e }","preventionTips":["Clamp startLine to the freshly-read totalLines before paginating.","Treat files that may rotate as volatile and re-read totalLines each page.","Use startLine=1 for empty-file reads (the code carves out that special case)."],"tags":["sandbox","file-io","pagination","typescript"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}