CherryHQ/cherry-studio · warning · Error

Path not found: ${targetPath}

Error message

Path not found: ${targetPath}

What it means

fs.stat on the validated deletion target rejected with code ENOENT — the path resolved inside the workspace root but nothing exists there. Thrown after validatePath succeeds, so this is purely an on-disk existence check, not a sandboxing failure. The user-facing path (targetPath) is used in the message, not the resolved absolute path.

Source

Thrown at src/main/ai/mcp/servers/filesystem/tools/delete.ts:45

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

  const targetPath = parsed.data.path
  const validPath = await validatePath(targetPath, baseDir)
  const recursive = parsed.data.recursive || false

  // Check if path exists and get stats
  let stats
  try {
    stats = await fs.stat(validPath)
  } catch (error: any) {
    if (error.code === 'ENOENT') {
      throw new Error(`Path not found: ${targetPath}`)
    }
    throw error
  }

  const isDirectory = stats.isDirectory()
  const relativePath = path.relative(baseDir, validPath)

  // Perform deletion
  try {
    if (isDirectory) {
      if (recursive) {
        // Delete directory recursively
        await fs.rm(validPath, { recursive: true, force: true })
      } else {
        // Try to delete empty directory
        await fs.rmdir(validPath)
      }
    } else {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Confirm the path still exists immediately before calling delete — list the parent directory first.
  2. If the path was just created in the same flow, watch for async ordering that deletes before creation completes.
  3. Treat a missing target as idempotent success if that matches the tool's semantics — swallow ENOENT instead of throwing.

Example fix

// before
try {
  stats = await fs.stat(validPath)
} catch (error: any) {
  if (error.code === 'ENOENT') {
    throw new Error(`Path not found: ${targetPath}`)
  }
  throw error
}

// after — optional idempotent mode for delete-if-exists semantics
if (options.idempotent) {
  return { content: [{ type: 'text', text: `Already absent: ${relativePath}` }] }
}
throw new Error(`Path not found: ${targetPath}`)
Defensive patterns

Strategy: validation

Validate before calling

// Check existence before attempting deletion to give a clearer message.
import { stat } from 'fs/promises'
async function assertExists(p: string): Promise<void> {
  try { await stat(p) } catch { throw new Error(`Path not found: ${p}`) }
}

Try / catch

// Treat ENOENT as idempotent success if 'delete-if-exists' semantics are desired.
try {
  stats = await fs.stat(validPath)
} catch (e: any) {
  if (e.code === 'ENOENT' && options.idempotent) return alreadyAbsentResult
  if (e.code === 'ENOENT') throw new Error(`Path not found: ${targetPath}`)
  throw e
}

Prevention

When it happens

Trigger: The caller passed a path to a file/directory that has already been deleted, was never created, or was misspelled. Also fires on TOCTOU races where the entry is removed between listing and deletion.

Common situations: A model deleting a file it previously read but that a concurrent process removed; a stale path from an earlier session; a relative-vs-absolute confusion that validatePath accepted but points at an empty slot.

Related errors


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