{"record":{"id":"78267950d3519dbd","repo":"laurent22/joplin","slug":"cannot-move-notebook-to-this-location","errorCode":null,"errorMessage":"Cannot move notebook to this location","messagePattern":"Cannot move notebook to this location","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/lib/models/Folder.ts","lineNumber":1053,"sourceCode":"\t\tif (isRootSharedFolder(folder)) return false;\n\n\t\tconst conflictFolderId = Folder.conflictFolderId();\n\t\tif (folderId === conflictFolderId || targetFolderId === conflictFolderId) return false;\n\n\t\tif (!targetFolderId) return true;\n\n\t\twhile (true) {\n\t\t\tconst folder = await Folder.load(targetFolderId);\n\t\t\tif (!folder.parent_id) break;\n\t\t\tif (folder.parent_id === folderId) return false;\n\t\t\ttargetFolderId = folder.parent_id;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic static async moveToFolder(folderId: string, targetFolderId: string) {\n\t\tif (!(await this.canNestUnder(folderId, targetFolderId))) throw new Error(_('Cannot move notebook to this location'));\n\n\t\tconst original = await this.load(folderId);\n\t\tconst modifiedFolder: FolderEntity = {\n\t\t\tid: folderId,\n\t\t\tparent_id: targetFolderId,\n\n\t\t\t// When moving a note to a different folder, the user timestamp is not updated.\n\t\t\t// However updated_time is updated so that the note can be synced later on.\n\t\t\tupdated_time: time.unixMs(),\n\t\t\tshare_id: original.share_id,\n\t\t};\n\n\t\tconst wasShared = !!modifiedFolder.share_id;\n\t\tconst movedToTopLevel = original.parent_id !== '' && targetFolderId === '';\n\t\tif (wasShared && movedToTopLevel) {\n\t\t\t// When a shared subfolder is converted to a toplevel folder, clear its share_id\n\t\t\t// as soon as possible. Without this, modifiedFolder would be incorrectly treated\n\t\t\t// as a root shared folder by some logic.","sourceCodeStart":1035,"sourceCodeEnd":1071,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/models/Folder.ts#L1035-L1071","documentation":"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 _().","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait Folder.moveToFolder(folderId, targetFolderId);\n// after - pre-validate and inform the user\nif (!(await Folder.canNestUnder(folderId, targetFolderId))) {\n  throw new Error('That notebook cannot be moved into its own descendant.');\n}\nawait Folder.moveToFolder(folderId, targetFolderId);","handlingStrategy":"validation","validationCode":"if (!(await Folder.canNestUnder(folderId, targetFolderId))) {\n  throw new Error('Move rejected: target would create a cycle or is a system folder.');\n}","typeGuard":"function isInvalidMove(e: any): boolean {\n  return e && typeof e.message === 'string' && /move notebook to this location/i.test(e.message);\n}","tryCatchPattern":"try {\n  await Folder.moveToFolder(folderId, targetFolderId);\n} catch (e) {\n  if (isInvalidMove(e)) {\n    // inform user, or pick a different target\n    return { moved: false, reason: 'cycle-or-system-folder' };\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["folder","move","cycle","validation","notebook"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}