laurent22/joplin · error · Error
Cannot find unique filename
Error message
Cannot find unique filename
What it means
Thrown by findUniqueFilename() in the base fs driver. It appends a counter (or, after 1000 tries, a timestamp) to generate a non-existent, non-reserved name. If after 1100 attempts every candidate still exists or is reserved, it gives up rather than loop infinitely.
Source
Thrown at packages/lib/fs-driver-base.ts:227
const nameNoExt = pathPrefix + filename(baseName);
let extension = fileExtension(baseName);
if (extension) extension = `.${extension}`;
let nameToTry = nameNoExt + extension;
while (true) {
// Check if the filename does not exist in the filesystem and is not reserved
const exists = await this.exists(nameToTry) || isReserved(nameToTry);
if (!exists) return nameToTry;
if (!markdownSafe) {
nameToTry = `${nameNoExt} (${counter})${extension}`;
} else {
nameToTry = `${nameNoExt}-${counter}${extension}`;
}
counter++;
if (counter >= 1000) {
nameToTry = `${nameNoExt} (${new Date().getTime()})${extension}`;
await time.msleep(10);
}
if (counter >= 1100) throw new Error('Cannot find unique filename');
}
}
public async removeAllThatStartWith(dirPath: string, filenameStart: string) {
if (!filenameStart || !dirPath) throw new Error('dirPath and filenameStart cannot be empty');
const stats = await this.readDirStats(dirPath);
for (const stat of stats) {
if (stat.path.indexOf(filenameStart) === 0) {
await this.remove(`${dirPath}/${stat.path}`);
}
}
}
public async waitTillExists(path: string, timeout = 10000) {
const startTime = Date.now();
View on GitHub (pinned to 2654b33620)
Solutions
- Clear or archive the target directory so existing-name collisions drop below 1000.
- Use a more distinctive base name (include a date or random component) before calling findUniqueFilename.
- Review and trim the reservedNames list if it's catching legitimate candidates.
- If you control the caller, fall back to a UUID-based name instead of the counter loop.
Example fix
// before
const name = await shim.fsDriver().findUniqueFilename(dir, baseName);
// after - prepend a unique stamp to avoid the 1100-try ceiling
const stamped = `${baseName}-${Date.now()}`;
const name = await shim.fsDriver().findUniqueFilename(dir, stamped); Defensive patterns
Strategy: validation
Validate before calling
// Cap how many colliding names exist in the target dir before generating.
const colliding = (await shim.fsDriver().readDirStats(dir)).filter(s => s.path.indexOf(baseName) === 0).length;
if (colliding > 900) throw new Error(`Too many name collisions for ${baseName}; clear the directory.`); Type guard
function isUniqueFilenameExhausted(e: any): boolean {
return e && typeof e.message === 'string' && e.message === 'Cannot find unique filename';
} Try / catch
let name;
try {
name = await shim.fsDriver().findUniqueFilename(dir, baseName);
} catch (e) {
if (isUniqueFilenameExhausted(e)) {
name = `${dir}/${baseName}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`;
} else throw e;
} Prevention
- Clear or archive directories before bulk exports to avoid thousands of collisions.
- Use distinctive base names (include dates or IDs) when generating many files.
- Prefer UUID-based names for programmatic bulk creation.
- Trim the reservedNames list if it's overly broad.
When it happens
Trigger: Calling findUniqueFilename for a directory containing thousands of files with the same base name and incrementing pattern, or with many reserved names colliding with the generated candidates.
Common situations: Exporting/importing thousands of notes with identical titles; a directory polluted with stale 'name (N).md' files from prior exports; reserved-name list overly broad and colliding; pathological filenames; a bug creating files faster than uniqueness can be found.
Related errors
- Format "${format}" can only be exported to a file
- Only one output directory can be selected
- Please specify the sync target path.
- HTML export is not supported. Please use the desktop applica
- Cannot find "%s".
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/0653435174745810.
Report an issue: GitHub.