laurent22/joplin · warning · Error
Cannot move notebook to this location
Error message
Cannot move notebook to this location
What it means
Thrown by Folder.moveToFolder() when canNestUnder() returns false. canNestUnder walks the target's ancestor chain and rejects moves that would create a cycle (moving a folder into itself or one of its descendants), plus moving into the conflict folder, a root shared folder, or the same folder. The error message is localized via _().
Source
Thrown at packages/lib/models/Folder.ts:1053
if (isRootSharedFolder(folder)) return false;
const conflictFolderId = Folder.conflictFolderId();
if (folderId === conflictFolderId || targetFolderId === conflictFolderId) return false;
if (!targetFolderId) return true;
while (true) {
const folder = await Folder.load(targetFolderId);
if (!folder.parent_id) break;
if (folder.parent_id === folderId) return false;
targetFolderId = folder.parent_id;
}
return true;
}
public static async moveToFolder(folderId: string, targetFolderId: string) {
if (!(await this.canNestUnder(folderId, targetFolderId))) throw new Error(_('Cannot move notebook to this location'));
const original = await this.load(folderId);
const modifiedFolder: FolderEntity = {
id: folderId,
parent_id: targetFolderId,
// When moving a note to a different folder, the user timestamp is not updated.
// However updated_time is updated so that the note can be synced later on.
updated_time: time.unixMs(),
share_id: original.share_id,
};
const wasShared = !!modifiedFolder.share_id;
const movedToTopLevel = original.parent_id !== '' && targetFolderId === '';
if (wasShared && movedToTopLevel) {
// When a shared subfolder is converted to a toplevel folder, clear its share_id
// as soon as possible. Without this, modifiedFolder would be incorrectly treated
// as a root shared folder by some logic.View on GitHub (pinned to 2654b33620)
Solutions
- Call Folder.canNestUnder(folderId, targetFolderId) first and surface a user-friendly message if it returns false.
- Block the UI action when the target is the folder itself or one of its descendants (compute descendants once).
- Disallow dropping onto system folders (conflict, shared root, trash).
- Reload the folder tree if it may be stale, then retry the validation.
Example fix
// before
await Folder.moveToFolder(folderId, targetFolderId);
// after - pre-validate and inform the user
if (!(await Folder.canNestUnder(folderId, targetFolderId))) {
throw new Error('That notebook cannot be moved into its own descendant.');
}
await Folder.moveToFolder(folderId, targetFolderId); Defensive patterns
Strategy: validation
Validate before calling
if (!(await Folder.canNestUnder(folderId, targetFolderId))) {
throw new Error('Move rejected: target would create a cycle or is a system folder.');
} Type guard
function isInvalidMove(e: any): boolean {
return e && typeof e.message === 'string' && /move notebook to this location/i.test(e.message);
} Try / catch
try {
await Folder.moveToFolder(folderId, targetFolderId);
} catch (e) {
if (isInvalidMove(e)) {
// inform user, or pick a different target
return { moved: false, reason: 'cycle-or-system-folder' };
}
throw e;
} Prevention
- Always call Folder.canNestUnder before moveToFolder.
- Disable drop targets that are the folder itself or its descendants in the UI.
- Forbid dropping onto system folders (conflict, shared root, trash).
- Refresh the folder tree if it may be stale before validating.
When it happens
Trigger: Calling moveToFolder(folderId, targetFolderId) where targetFolderId is folderId itself, or a descendant of folderId, or the conflict folder, or a root shared folder — i.e. any placement that breaks the folder tree invariant.
Common situations: Drag-and-drop UI where the drop target is a subfolder of the dragged folder; programmatic re-parenting without cycle checks; attempting to nest under the conflict or shared-root system folder; race where the folder was moved elsewhere concurrently.
Related errors
- Cannot move item with type ${itemType}
- Cannot find "%s".
- No folder with id ${id}
- The trash folder cannot be deleted
- Parent ID cannot be the same as ID
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/78267950d3519dbd.
Report an issue: GitHub.