CherryHQ/cherry-studio · warning · Error

Path is not a file: ${filePath}

Error message

Path is not a file: ${filePath}

What it means

fs.stat on the validated edit target succeeded but stats.isFile() returned false — the path resolves to a directory (or other non-file type) but the edit tool requires a regular file. The user-facing filePath appears in the message. This is a semantic mismatch, not a missing file (ENOENT is handled separately at edit.ts:51).

Source

Thrown at src/main/ai/mcp/servers/filesystem/tools/edit.ts:48

}

// Handler implementation
export async function handleEditTool(args: unknown, baseDir: string) {
  const parsed = EditToolSchema.safeParse(args)
  if (!parsed.success) {
    throw new Error(`Invalid arguments for edit: ${parsed.error}`)
  }

  const { file_path: filePath, old_string: oldString, new_string: newString, replace_all: replaceAll } = parsed.data

  // Validate 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') {
      // If old_string is empty, this is a create new file operation
      if (oldString === '') {
        // Create parent directory if needed
        const parentDir = path.dirname(validPath)
        await fs.mkdir(parentDir, { recursive: true })

        // Write the new content
        await fs.writeFile(validPath, newString, 'utf-8')

        logger.info('File created', { path: validPath })

        const relativePath = path.relative(baseDir, validPath)
        return {
          content: [
            {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Confirm the path points to a regular file — use the ls or read tool first.
  2. If the intent is to edit multiple files, call edit once per file with a concrete file path.
  3. Strip any trailing slash from the path before sending, since directories are often written that way.

Example fix

// before
if (!stats.isFile()) {
  throw new Error(`Path is not a file: ${filePath}`)
}

// after — say what it actually is
const kind = stats.isDirectory() ? 'directory' : stats.isSymbolicLink() ? 'symlink' : 'non-file'
throw new Error(`Path is a ${kind}, not a file: ${filePath}`)
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the target is a regular file before editing.
import { stat } from 'fs/promises'
async function assertIsFile(p: string): Promise<void> {
  const s = await stat(p)
  if (!s.isFile()) throw new Error(`Path is not a file: ${p}`)
}

Prevention

When it happens

Trigger: The caller passed a directory path to the edit tool, expecting it to edit 'all files inside' or treating a directory like a file. Also fires for special files (device, socket) that are not regular files.

Common situations: A model confusing a directory path (e.g. '/src/components') with a file path ('/src/components/Button.tsx'); passing a path that points at a symlink to a directory.

Related errors


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