modelcontextprotocol/servers · error · Error

Parent directory does not exist: ${parentDir}

Error message

Parent directory does not exist: ${parentDir}

What it means

Thrown by `validatePath` when the target file does not exist (ENOENT) AND its parent directory also does not exist (realpath of the parent throws). This distinguishes a legitimate new-file creation (parent exists) from an impossible one (parent missing), preventing creation cascades into nonexistent locations.

Source

Thrown at src/filesystem/lib.ts:135

    const normalizedReal = normalizePath(realPath);
    if (!isPathWithinAllowedDirectories(normalizedReal, allowedDirectories)) {
      throw new Error(`Access denied - symlink target outside allowed directories: ${realPath} not in ${allowedDirectories.join(', ')}`);
    }
    return realPath;
  } catch (error) {
    // Security: For new files that don't exist yet, verify parent directory
    // This ensures we can't create files in unauthorized locations
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
      const parentDir = path.dirname(absolute);
      try {
        const realParentPath = await fs.realpath(parentDir);
        const normalizedParent = normalizePath(realParentPath);
        if (!isPathWithinAllowedDirectories(normalizedParent, allowedDirectories)) {
          throw new Error(`Access denied - parent directory outside allowed directories: ${realParentPath} not in ${allowedDirectories.join(', ')}`);
        }
        return absolute;
      } catch {
        throw new Error(`Parent directory does not exist: ${parentDir}`);
      }
    }
    throw error;
  }
}


// File Operations
export async function getFileStats(filePath: string): Promise<FileInfo> {
  const stats = await fs.stat(filePath);
  return {
    size: stats.size,
    created: stats.birthtime,
    modified: stats.mtime,
    accessed: stats.atime,
    isDirectory: stats.isDirectory(),
    isFile: stats.isFile(),
    permissions: stats.mode.toString(8).slice(-3),

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Create the parent directory first with `create_directory`, then write the file.
  2. Verify the full parent path exists before issuing the write.
  3. Check for typos in intermediate directory names.

Example fix

// before
write_file({ path: '/home/me/projects/new/sub/x.txt', content: 'hi' })
// after
create_directory({ path: '/home/me/projects/new/sub' })
write_file({ path: '/home/me/projects/new/sub/x.txt', content: 'hi' })
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs/promises';
async function parentExists(p: string): Promise<boolean> {
  try { await fs.access(path.dirname(p)); return true; } catch { return false; }
}
if (!await parentExists(args.path)) {
  await callTool('create_directory', { path: path.dirname(args.path) });
}

Try / catch

try {
  await writeFile({ path, content });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Parent directory does not exist')) {
    await createDirectory({ path: dirname(path) });
    await writeFile({ path, content });
  }
}

Prevention

When it happens

Trigger: Calling a write/create tool with a path whose parent directory does not exist on disk — e.g. `write_file({ path: '/home/me/projects/nested/deep/x.txt' })` when `nested/deep` was never created.

Common situations: Forgetting to `create_directory` first, typos in intermediate path segments, or assuming a directory exists when it doesn't. The catch-all around the parent realpath turns any failure (including ENOENT of the parent) into this message.

Related errors


AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12). Data as JSON: /api/errors/8054b5f7dfebeda7. Report an issue: GitHub.