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

  1. Verify the file exists (ls / fs.access) at the exact resolved path before calling read.
  2. Check that any prerequisite build/generation step that produces the file has completed.
  3. 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

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


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/e8113b10d6cd28b1. Report an issue: GitHub.