CherryHQ/cherry-studio · warning · Error
Directory not empty: ${targetPath}. Use recursive=true to de
Error message
Directory not empty: ${targetPath}. Use recursive=true to delete non-empty directories. What it means
fs.rmdir on a non-empty directory rejected with code ENOTEMPTY because the caller set recursive=false (or omitted it, defaulting to false) but the target directory still contains entries. The message tells the caller to pass recursive=true. Caught by the server-level handler and returned as an isError tool result.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/delete.ts:69
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 {
// Delete file
await fs.unlink(validPath)
}
} catch (error: any) {
if (error.code === 'ENOTEMPTY') {
throw new Error(`Directory not empty: ${targetPath}. Use recursive=true to delete non-empty directories.`)
}
throw new Error(`Failed to delete: ${error.message}`)
}
// Log the operation
logger.info('Path deleted', {
path: validPath,
type: isDirectory ? 'directory' : 'file',
recursive: isDirectory ? recursive : undefined
})
// Format output
const itemType = isDirectory ? 'Directory' : 'File'
const recursiveNote = isDirectory && recursive ? ' (recursive)' : ''
return {
content: [
{View on GitHub (pinned to 726446b54c)
Solutions
- Re-invoke delete with recursive:true if intentional whole-tree removal is desired.
- If only an empty directory should be removed, first list it (ls tool) and confirm it has no entries, including hidden ones.
- Audit for hidden files like .DS_Store, .gitkeep, or editor temp files that make an 'empty' directory non-empty.
Example fix
// before
throw new Error(`Directory not empty: ${targetPath}. Use recursive=true to delete non-empty directories.`)
// after — include the entry count so the caller knows the scale
const entries = await fs.readdir(validPath)
throw new Error(`Directory not empty: ${targetPath} has ${entries.length} entries. Use recursive=true to delete.`) Defensive patterns
Strategy: validation
Validate before calling
// Detect a non-empty directory before calling delete without recursive.
import { readdir } from 'fs/promises'
async function isDirEmpty(p: string): Promise<boolean> {
const entries = await readdir(p)
return entries.length === 0
}
if (stats.isDirectory() && !recursive) {
const empty = await isDirEmpty(validPath)
if (!empty) throw new Error('Pass recursive=true to delete non-empty directory')
} Prevention
- Pass recursive:true whenever deleting a directory that may contain entries.
- Remember hidden files (.DS_Store, .gitkeep) make a directory non-empty.
- List the directory first if you need to guard against accidental bulk deletion.
- Treat ENOTEMPTY as a recoverable prompt for confirmation, not a hard failure.
When it happens
Trigger: Calling delete on a directory that contains files or subdirectories without setting recursive:true. The handler first fs.stat'd the target as a directory, then chose fs.rmdir over fs.rm because the recursive flag was false.
Common situations: A model assuming delete is recursive by default; an empty-looking directory that still holds hidden files (e.g. .DS_Store) or a .git folder; a directory the user expected to be empty but contains nested output.
Related errors
- Path not found: ${targetPath}
- Unknown tool: ${name}
- Invalid arguments for delete: ${parsed.error}
- Failed to delete: ${error.message}
- Invalid arguments for edit: ${parsed.error}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/9718ebb564a4ec4c.
Report an issue: GitHub.