CherryHQ/cherry-studio · error · Error
Failed to create parent directory: ${error.message}
Error message
Failed to create parent directory: ${error.message} What it means
Thrown by the write tool when fs.mkdir(parentDir, {recursive:true}) fails with an error code other than EEXIST. EEXIST is intentionally swallowed, but any other failure (EACCES permission denied, ENAMETOOLONG, ENOTDIR when a path component is a file not a directory, EROFS on read-only filesystem, ENOSPC) is wrapped and re-thrown, aborting the write before the file is created.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/write.ts:43
}
// Handler implementation
export async function handleWriteTool(args: unknown, baseDir: string) {
const parsed = WriteToolSchema.safeParse(args)
if (!parsed.success) {
throw new Error(`Invalid arguments for write: ${parsed.error}`)
}
const filePath = parsed.data.file_path
const validPath = await validatePath(filePath, baseDir)
// Create parent directory if it doesn't exist
const parentDir = path.dirname(validPath)
try {
await fs.mkdir(parentDir, { recursive: true })
} catch (error: any) {
if (error.code !== 'EEXIST') {
throw new Error(`Failed to create parent directory: ${error.message}`)
}
}
// Check if file exists (for logging)
let isOverwrite = false
try {
await fs.stat(validPath)
isOverwrite = true
} catch {
// File doesn't exist, that's fine
}
// Write the file
try {
await fs.writeFile(validPath, parsed.data.content, 'utf-8')
} catch (error: any) {
throw new Error(`Failed to write file: ${error.message}`)
}View on GitHub (pinned to 726446b54c)
Solutions
- Confirm the parent directory is writable by the process and not on a read-only filesystem.
- Check that no path segment is a regular file (ENOTDIR) by listing the parent chain.
- Shorten overly deep paths or relax the sandbox/ACL for the workspace root.
- Inspect error.message (it carries the underlying errno/code) to identify EACCES vs EROFS vs ENOTDIR.
Example fix
// before: baseDir on read-only mount
await handleWriteTool({ file_path: '/readonly/out.txt', content: 'x' }, baseDir) // throws: Failed to create parent directory
// after: point baseDir at a writable workspace
await handleWriteTool({ file_path: '/workspace/out.txt', content: 'x' }, writableBaseDir) Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'fs/promises'
async function ensureParentWritable(filePath: string): Promise<void> {
const parent = path.dirname(filePath)
await fs.mkdir(parent, { recursive: true }) // surface errors early
await fs.access(parent, fs.constants.W_OK)
} Try / catch
try {
await handleWriteTool(args, baseDir)
} catch (e) {
const msg = e instanceof Error ? e.message : String(e)
if (msg.startsWith('Failed to create parent directory')) {
// inspect errno: EACCES -> permissions, EROFS -> read-only, ENOTDIR -> path-segment-is-file
} else throw e
} Prevention
- Confirm the workspace root is on a writable filesystem before enabling the write tool.
- Avoid building paths with arbitrary user segments that can collide with existing files (ENOTDIR).
- Watch for sandbox/ACL changes after app or OS updates.
When it happens
Trigger: Writing to a path whose parent chain crosses a file (ENOTDIR), into a directory the process lacks permission to create (EACCES), onto a read-only volume (EROFS), with a path longer than OS limits (ENAMETOOLONG), or when the disk is out of space/inodes at mkdir time.
Common situations: baseDir misconfigured to a read-only mount; parent path accidentally contains a filename segment; sandboxed Electron process with restricted filesystem ACLs; very deep nested paths generated programmatically; cross-device path after a config change.
Related errors
- Failed to write file: ${error.message}
- Invalid arguments for write: ${parsed.error}
- InternalError
- ${packageLabel} file not found
- Failed to read journal at ${journalPath}: ${(err as Error).m
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/5c7214c713b87f80.
Report an issue: GitHub.