modelcontextprotocol/servers · error · Error

Parent directory does not exist: ${path.dirname(absolute)}

Error message

Parent directory does not exist: ${path.dirname(absolute)}

What it means

When the requested path does not exist (realpath throws ENOENT), validatePath falls back to resolveUnicodeEquivalentPath to support mkdir -p style creation. If that fallback also fails with ENOENT, the nearest existing ancestor could not be resolved — meaning the parent directory chain is missing — so this error is thrown instead of silently creating directories in the wrong place.

Source

Thrown at src/filesystem/lib.ts:177

  // Security: Handle symlinks by checking their real path to prevent symlink attacks
  // This prevents attackers from creating symlinks that point outside allowed directories
  try {
    const realPath = await fs.realpath(absolute);
    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') {
      try {
        return await resolveUnicodeEquivalentPath(absolute);
      } catch (resolutionError) {
        if ((resolutionError as NodeJS.ErrnoException).code === 'ENOENT') {
          throw new Error(`Parent directory does not exist: ${path.dirname(absolute)}`);
        }
        throw resolutionError;
      }
    }
    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(),

View on GitHub (pinned to 579c3903f3)

Solutions

  1. Create the parent directory chain first (create_directory on the parent, then the target)
  2. Fix the typo/incorrect parent path in the request
  3. Verify each ancestor exists with list_directory or get_file_info before creating children

Example fix

// before: parents missing
await create_directory('/allowed/projects/app/src'); // ENOENT at 'app'
// after: create incrementally
await create_directory('/allowed/projects/app');
await create_directory('/allowed/projects/app/src');
Defensive patterns

Strategy: try-catch

Validate before calling

const parent = path.dirname(p);
try { await fs.access(parent); } catch {
  throw new Error(`Create parent ${parent} before ${p}`);
}

Try / catch

try {
  await create_directory(p);
} catch (err) {
  if (err.message.startsWith('Parent directory does not exist')) {
    await create_directory(err.message.split(': ')[1]); // create parent, then retry
    await create_directory(p);
  } else throw err;
}

Prevention

When it happens

Trigger: create_directory or write-style calls targeting '/allowed/a/b/newdir' where the parent '/allowed/a/b' (or an earlier ancestor) does not exist, or where a symlinked ancestor chain is broken.

Common situations: Typos in the parent directory name; assuming the tool creates intermediate directories; a directory deleted between planning and execution; Unicode-normalized parents that fail to match (leading to nested ENOENT).

Related errors


AI-assisted analysis of modelcontextprotocol/servers@579c3903f3 (2026-09-01). Data as JSON: /api/errors/68e85a54d059db50. Report an issue: GitHub.