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
- List the directory and remove/rename the duplicate Unicode-equivalent entries so only one NFC-normalized spelling remains
- Use the exact on-disk spelling of the component (verified with readdir) so the exactMatch branch short-circuits ambiguity
- 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
- Normalize filenames to NFC when creating/syncing files
- Periodically scan allowed roots for NFC-duplicate names
- Always reference files by their exact readdir-reported names
- Disable tools that decompose filenames (macOS NFD) when sharing with Linux
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
- Parent directory does not exist: ${path.dirname(absolute)}
- Parent directory does not exist: ${parentDir}
- Could not find exact match for edit:\n${edit.oldText}
- Access denied - symlink target outside allowed directories:
- Access denied - Windows-style path received on a POSIX host:
AI-assisted analysis of modelcontextprotocol/servers@579c3903f3 (2026-09-01).
Data as JSON: /api/errors/38f9e6dd35c98e15.
Report an issue: GitHub.