CherryHQ/cherry-studio · error · Error
File not found: ${filePath}
Error message
File not found: ${filePath} What it means
Thrown by the read tool when fs.stat rejects with error.code === 'ENOENT'. validatePath already succeeded (the path is within the workspace root), but the file does not exist on disk. This is distinct from a workspace-access denial (error 327) and from the not-a-file case (error 320): validation passed, the inode is simply absent.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/read.ts:48
// Handler implementation
export async function handleReadTool(args: unknown, baseDir: string) {
const parsed = ReadToolSchema.safeParse(args)
if (!parsed.success) {
throw new Error(`Invalid arguments for read: ${parsed.error}`)
}
const filePath = parsed.data.file_path
const validPath = await validatePath(filePath, baseDir)
// Check if file exists
try {
const stats = await fs.stat(validPath)
if (!stats.isFile()) {
throw new Error(`Path is not a file: ${filePath}`)
}
} catch (error: any) {
if (error.code === 'ENOENT') {
throw new Error(`File not found: ${filePath}`)
}
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) {View on GitHub (pinned to 726446b54c)
Solutions
- Verify the file exists (ls / fs.access) at the exact resolved path before calling read.
- Check that any prerequisite build/generation step that produces the file has completed.
- Confirm baseDir is the workspace root you expect, since relative paths resolve against it.
Example fix
// before
await handleReadTool({ file_path: 'src/missing.ts' }, baseDir) // throws: File not found
// after
import fs from 'fs/promises'
await fs.access(path.join(baseDir, 'src/index.ts')) // confirm first
await handleReadTool({ file_path: 'src/index.ts' }, baseDir) Defensive patterns
Strategy: validation
Validate before calling
import fs from 'fs/promises'
async function exists(p: string): Promise<boolean> {
try { await fs.access(p); return true } catch { return false }
}
// guard: if (!(await exists(resolved))) return Try / catch
try {
await handleReadTool(args, baseDir)
} catch (e) {
if (e instanceof Error && e.message.startsWith('File not found')) {
// treat as missing-resource, skip or report
} else throw e
} Prevention
- Cache file-existence at the source and refresh before each read if the workspace is volatile.
- When paginating, stop when the prior read reports no more lines rather than guessing offsets.
- Log the resolved absolute path on failure to spot typos and wrong baseDir quickly.
When it happens
Trigger: Calling handleReadTool with a file_path inside the workspace root that has not been created, was deleted between validation and stat, or is misspelled. Also fires when realpath-style resolution in validatePath succeeds against a parent directory but the leaf file is missing.
Common situations: Reading a file before it is generated by a build step; typo in filename; race where the file is removed by another process between validatePath and fs.stat; relative path resolved against an unexpected baseDir.
Related errors
- Path is not a file: ${filePath}
- Cannot read binary file: ${filePath}
- Invalid offset: ${offset + 1}. File has ${lines.length} line
- 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/e8113b10d6cd28b1.
Report an issue: GitHub.