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
- Confirm the path still exists immediately before calling delete — list the parent directory first.
- If the path was just created in the same flow, watch for async ordering that deletes before creation completes.
- 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
- List the parent directory before deleting to confirm the entry exists.
- Watch for TOCTOU races in concurrent flows that create and delete the same path.
- Consider idempotent delete semantics if your tool contract allows it.
- Use absolute paths to avoid resolution surprises.
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
- Directory not empty: ${targetPath}. Use recursive=true to de
- File not found: ${filePath}
- Directory not found: ${validPath}
- Session workspace is unavailable: ${workspaceRoot}
- File not found in workspace: ${userPath}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/609455f792b66339.
Report an issue: GitHub.