laurent22/joplin · error · Error
Notebooks cannot be named "%s", which is a reserved title.
Error message
Notebooks cannot be named "%s", which is a reserved title.
What it means
Thrown by Folder.save when options.reservedTitleCheck is true and o.title exactly equals Folder.conflictFolderTitle(), which returns the localized string for 'Conflicts' (packages/lib/models/Folder.ts:195). That title is reserved for Joplin's built-in conflict notebook (Folder.conflictFolder / getConflictFolderId), so letting a user create one would collide with the reserved id and corrupt conflict resolution. reservedTitleCheck defaults on only when userSideValidation is true.
Source
Thrown at packages/lib/models/Folder.ts:1118
}
}
// We allow folders with duplicate titles so that folders with the same title can exist under different parent folder. For example:
//
// PHP
// Code samples
// Doc
// Java
// My project
// Doc
// if (options.duplicateCheck === true && o.title) {
// let existingFolder = await Folder.loadByTitle(o.title);
// if (existingFolder && existingFolder.id != o.id) throw new Error(_('A notebook with this title already exists: "%s"', o.title));
// }
if (options.reservedTitleCheck === true && o.title) {
if (o.title === Folder.conflictFolderTitle()) throw new Error(_('Notebooks cannot be named "%s", which is a reserved title.', o.title));
}
syncDebugLog.info('Folder Save:', o);
let savedFolder: FolderEntity = await super.save(o, options);
// Ensures that any folder added to the state has all the required
// properties, in particular "share_id" and "parent_id', which are
// required in various parts of the code.
if (!('share_id' in savedFolder) || !('parent_id' in savedFolder) || !('deleted_time' in savedFolder)) {
savedFolder = await this.load(savedFolder.id);
}
this.dispatch({
type: 'FOLDER_UPDATE_ONE',
item: savedFolder,
});
View on GitHub (pinned to 2654b33620)
Solutions
- In the rename/create UI, block or auto-suffix titles equal to Folder.conflictFolderTitle() before calling save.
- If the user genuinely wants a notebook named 'Conflicts', pick an alternate spelling or suffix (e.g. 'Conflicts (personal)') — the exact reserved string is the only blocked value.
- For non-user-initiated persistence (import/sync replay), omit userSideValidation/reservedTitleCheck so the reserved check does not apply.
- Compare against the localized conflictFolderTitle() (not a hard-coded English string) so the guard is correct across locales.
Example fix
// before
folder.title = newTitle;
await Folder.save(folder, { userSideValidation: true });
// after
if (newTitle === Folder.conflictFolderTitle()) {
throw new Error(`The name "${newTitle}" is reserved; choose a different notebook title.`);
}
folder.title = newTitle;
await Folder.save(folder, { userSideValidation: true }); Defensive patterns
Strategy: validation
Validate before calling
// Block the reserved conflict title (locale-aware) before saving.
function isReservedTitle(title: string): boolean {
return Boolean(title) && title === Folder.conflictFolderTitle();
}
if (isReservedTitle(folder.title)) {
throw new Error(`"${folder.title}" is a reserved notebook name.`);
} Try / catch
try {
await Folder.save(folder, { userSideValidation: true });
} catch (error) {
if (error.message.startsWith('Notebooks cannot be named')) {
// Prompt the user for a different name; re-render the rename dialog.
return showRenameError(error.message);
}
throw error;
} Prevention
- Compare candidate titles against Folder.conflictFolderTitle() (the localized value) rather than a hard-coded English 'Conflicts'.
- Disable the save/confirm button in the UI when the entered title equals the reserved conflict title.
- Reserve the conflict title at the form-validation layer so the user never submits it, not just at the model layer.
- For import/sync code that intentionally persists arbitrary titles, omit reservedTitleCheck / userSideValidation rather than patching titles.
When it happens
Trigger: Calling Folder.save(folder, { userSideValidation: true }) (which auto-enables reservedTitleCheck) — or passing reservedTitleCheck: true directly — with o.title set to the localized word for 'Conflicts' (e.g. 'Conflicts' in English, the translated term in other locales). Exact, case-sensitive equality only.
Common situations: A user typing 'Conflicts' as a notebook name in the UI; a rename operation hitting the reserved word; localized builds where the user typed the translated conflict title; import scripts that create notebooks from arbitrary external names without filtering reserved words.
Related errors
- Parent ID cannot be the same as ID
- The trash folder cannot be deleted
- Cannot move notebook to this location
- More than one item match "%s". Please narrow down your query
- Cannot find "%s".
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/51cef207cfee450c.
Report an issue: GitHub.