CherryHQ/cherry-studio · error · Error
Invalid offset: ${offset + 1}. File has ${lines.length} line
Error message
Invalid offset: ${offset + 1}. File has ${lines.length} lines. What it means
Thrown by the read tool when the 0-based offset (parsed.data.offset converted from 1-based, defaulting to 0) is negative or greater-than-or-equal to the number of lines. Line count comes from content.split('\n'), so an empty file yields length 1 (a single empty string). Any offset past the last line, or a negative offset from a bad input, triggers this before slicing.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/read.ts:67
}
throw error
}
// Check if file is binary
if (await isBinaryFile(validPath)) {
throw new Error(`Cannot read binary file: ${filePath}`)
}
// Read file content
const content = await fs.readFile(validPath, 'utf-8')
const lines = content.split('\n')
// Apply offset and limit
const offset = (parsed.data.offset || 1) - 1 // Convert to 0-based
const limit = parsed.data.limit || DEFAULT_READ_LIMIT
if (offset < 0 || offset >= lines.length) {
throw new Error(`Invalid offset: ${offset + 1}. File has ${lines.length} lines.`)
}
const selectedLines = lines.slice(offset, offset + limit)
// Format output with line numbers and truncate long lines
const output: string[] = []
const relativePath = path.relative(baseDir, validPath)
output.push(`File: ${relativePath}`)
if (offset > 0 || limit < lines.length) {
output.push(`Lines ${offset + 1} to ${Math.min(offset + limit, lines.length)} of ${lines.length}`)
}
output.push('')
selectedLines.forEach((line, index) => {
const lineNumber = offset + index + 1
const truncatedLine = line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) + '...' : line
output.push(`${lineNumber.toString().padStart(6)}\t${truncatedLine}`)View on GitHub (pinned to 726446b54c)
Solutions
- Cap offset at the file's line count before calling, or omit it to read from the start.
- Never pass offset=0; the schema is 1-based, so use offset >= 1 (or omit it for the default of 1).
- When paginating, derive the next offset from the previous read response and stop when it reports no more lines.
Example fix
// before
await handleReadTool({ file_path: 'small.txt', offset: 500 }, baseDir) // throws: Invalid offset
// after
await handleReadTool({ file_path: 'small.txt' }, baseDir) // omit offset, read from start Defensive patterns
Strategy: validation
Validate before calling
const MAX = Number.MAX_SAFE_INTEGER
function clampOffset(requested: number | undefined, lineCount: number): number | undefined {
if (requested === undefined) return undefined // default of 1 is always safe
if (requested < 1) return 1
if (requested > lineCount) return lineCount
return requested
} Type guard
function isValidOffset(offset: number | undefined, lineCount: number): boolean {
const o = (offset ?? 1) - 1
return o >= 0 && o < Math.max(lineCount, 1)
} Try / catch
try {
await handleReadTool(args, baseDir)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid offset')) {
// retry with offset omitted (read from start)
} else throw e
} Prevention
- Never pass offset=0; the schema is 1-based.
- Derive the next page offset from the previous read's 'more lines' hint, not from a guessed total.
- For very small files, omit offset/limit entirely.
When it happens
Trigger: Calling read with offset larger than the file's line count (e.g. offset=500 on a 10-line file); offset=0 which becomes -1 after the 1-based conversion; reading a near-empty file with offset >= 2. Note offset defaults to 1 (0-based 0), so omitting offset is always safe.
Common situations: Caller computed offset from a stale line count (file shrunk since); offset passed as 0 by a caller assuming 0-based indexing; paginating past EOF without tracking the prior response's 'more lines' hint; empty file plus offset >= 2.
Related errors
- Path is not a file: ${filePath}
- File not found: ${filePath}
- Cannot read binary file: ${filePath}
- Invalid arguments for write: ${parsed.error}
- Access denied: Path is outside the configured workspace root
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/a7bb1185d776602e.
Report an issue: GitHub.