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

  1. Confirm the parent directory is writable by the process and not on a read-only filesystem.
  2. Check that no path segment is a regular file (ENOTDIR) by listing the parent chain.
  3. Shorten overly deep paths or relax the sandbox/ACL for the workspace root.
  4. 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

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


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