modelcontextprotocol/servers · error · Error

Ambiguous Unicode path component: ${requestedPart}

Error message

Ambiguous Unicode path component: ${requestedPart}

What it means

resolveUnicodeEquivalentPath walks the target path component by component. When an exact directory-entry match is absent but multiple entries normalize (NFC) to the same component, it refuses to guess which one was meant and throws this error. This protects against picking the wrong file among Unicode-equivalent names (e.g. 'cafe\u0301' vs 'caf\u00e9').

Source

Thrown at src/filesystem/lib.ts:121

    .find(directory => isPathWithinAllowedDirectories(normalizePath(absolutePath), [directory]));

  if (!allowedDirectory) {
    return absolutePath;
  }

  let currentPath = await fs.realpath(allowedDirectory);
  const relativeParts = path.relative(allowedDirectory, absolutePath).split(path.sep).filter(Boolean);

  for (let index = 0; index < relativeParts.length; index++) {
    const requestedPart = relativeParts[index];
    const entries = (await fs.readdir(currentPath)) ?? [];
    const exactMatch = entries.find(entry => entry === requestedPart);
    const equivalentMatches = exactMatch
      ? [exactMatch]
      : entries.filter(entry => entry.normalize('NFC') === requestedPart.normalize('NFC'));

    if (equivalentMatches.length > 1) {
      throw new Error(`Ambiguous Unicode path component: ${requestedPart}`);
    }

    if (equivalentMatches.length === 0) {
      // Nothing below this point exists yet, so there are no symlinks left to
      // resolve. currentPath is already realpath'd and inside an allowed
      // directory; append the missing tail so create_directory can mkdir -p it.
      return path.join(currentPath, ...relativeParts.slice(index));
    }

    currentPath = await fs.realpath(path.join(currentPath, equivalentMatches[0]));
    if (!isPathWithinAllowedDirectories(normalizePath(currentPath), allowedDirectories)) {
      throw new Error(`Access denied - symlink target outside allowed directories: ${currentPath} not in ${allowedDirectories.join(', ')}`);
    }
  }

  return currentPath;
}

View on GitHub (pinned to 579c3903f3)

Solutions

  1. List the directory and remove/rename the duplicate Unicode-equivalent entries so only one NFC-normalized spelling remains
  2. Use the exact on-disk spelling of the component (verified with readdir) so the exactMatch branch short-circuits ambiguity
  3. Enable NFC normalization on the filesystem/sync tool that created the duplicate entries

Example fix

// before: requesting '/allowed/docs/caf\u00e9' when dir has 'caf\u00e9' (NFC) and 'cafe\u0301' (NFD)
await read_file('/allowed/docs/caf\u00e9');
// after: disambiguate to the exact on-disk name
await read_file('/allowed/docs/cafe\u0301'); // exact match, no ambiguity
Defensive patterns

Strategy: try-catch

Validate before calling

const entries = await fs.readdir(path.dirname(p));
const target = path.basename(p);
const exact = entries.includes(target);
const equiv = entries.filter(e => e.normalize('NFC') === target.normalize('NFC'));
if (!exact && equiv.length > 1) throw new Error(`Ambiguous Unicode component: ${target}`);

Type guard

function hasUniqueUnicodeMatch(entries: string[], target: string): boolean {
  if (entries.includes(target)) return true;
  return entries.filter(e => e.normalize('NFC') === target.normalize('NFC')).length === 1;
}

Try / catch

try {
  const resolved = await validatePath(p);
} catch (err) {
  if (err.message.startsWith('Ambiguous Unicode path component')) {
    const entries = await fs.readdir(path.dirname(p));
    // inspect entries and pick the exact intended spelling
  } else throw err;
}

Prevention

When it happens

Trigger: Calling any filesystem tool (via validatePath) for a path whose component doesn't exactly exist but where two or more sibling entries have the same NFC normalization, e.g. directory contains both decomposed and precomposed spellings of the same name.

Common situations: Files synced from macOS (NFD) alongside files created on Linux (NFC), archives extracted with both spellings, or git checkouts with core.precomposeUnicode disabled, leaving duplicate-looking names in one directory.

Related errors


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