{"record":{"id":"043133e69bde76a2","repo":"laurent22/joplin","slug":"the-trash-folder-cannot-be-deleted","errorCode":null,"errorMessage":"The trash folder cannot be deleted","messagePattern":"The trash folder cannot be deleted","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/lib/models/Folder.ts","lineNumber":143,"sourceCode":"\t\t\t'notes': Note,\n\t\t\t'resources': Resource,\n\t\t};\n\n\t\tfor (const tableName of ['folders', 'notes', 'resources']) {\n\t\t\tconst ItemClass = tableNameToClasses[tableName];\n\t\t\tconst rows = await this.db().selectAll(`SELECT id FROM ${tableName} WHERE share_id = ?`, [shareId]);\n\t\t\tconst ids: string[] = rows.map(r => r.id);\n\t\t\tawait ItemClass.batchDelete(ids, deleteOptions);\n\t\t}\n\t}\n\n\tpublic static async batchDelete(folderIds: string[], options: DeleteOptions): Promise<void> {\n\t\toptions = {\n\t\t\tdeleteChildren: true,\n\t\t\t...options,\n\t\t};\n\n\t\tif (folderIds.includes(getTrashFolderId())) throw new Error('The trash folder cannot be deleted');\n\n\t\tconst toTrash = !!options.toTrash;\n\n\t\tconst folders: FolderEntity[] = await Folder.loadItemsByIds(folderIds);\n\t\tif (!folders.length) return; // noop\n\n\t\tconst actionLogger = ActionLogger.from(options.sourceDescription);\n\t\tactionLogger.addDescription(`folder titles: ${JSON.stringify(folders.map(folder => folder.title))}`);\n\t\toptions.sourceDescription = actionLogger;\n\n\t\tif (options.deleteChildren) {\n\t\t\tconst childrenDeleteOptions: DeleteOptions = {\n\t\t\t\tdisableReadOnlyCheck: options.disableReadOnlyCheck,\n\t\t\t\tsourceDescription: actionLogger,\n\t\t\t\tdeleteChildren: true,\n\t\t\t\ttoTrash,\n\t\t\t};\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/models/Folder.ts#L125-L161","documentation":"Thrown by Folder.batchDelete() if the list of folder IDs to delete includes the trash folder ID. The trash folder is a system-managed, permanent container and must never be removed, so the driver hard-aborts before attempting the operation.","triggerScenarios":"Programmatic batch deletion that passes getTrashFolderId() in the folderIds array — e.g. a select-all-and-delete UI flow, a script iterating all folders, or a sync-driven bulk delete that inadvertently includes the trash.","commonSituations":"Custom automation or a plugin deletes every folder; a 'select all' UI action didn't filter out the trash; refactored code that previously only handled user notebooks now sees the trash; emptying trash logic recursed into the trash folder itself.","solutions":["Filter the trash folder ID out of the deletion list before calling batchDelete.","Use the dedicated emptyTrash() API to clear trash contents rather than deleting the trash folder.","In UI flows, exclude system folders (trash, conflict, shared root) from multi-select.","Audit custom scripts/plugins that enumerate folders for deletion."],"exampleFix":"// before\nawait Folder.batchDelete(allFolderIds, { toTrash: false });\n// after - exclude the trash folder\nconst deletable = allFolderIds.filter(id => id !== getTrashFolderId());\nawait Folder.batchDelete(deletable, { toTrash: false });","handlingStrategy":"validation","validationCode":"const safeIds = folderIds.filter(id => id !== getTrashFolderId());\nif (safeIds.length !== folderIds.length) {\n  throw new Error('Refusing to delete: trash folder ID was in the list.');\n}","typeGuard":"function isTrashDeleteBlocked(e: any): boolean {\n  return e && typeof e.message === 'string' && e.message === 'The trash folder cannot be deleted';\n}","tryCatchPattern":"try {\n  await Folder.batchDelete(ids, opts);\n} catch (e) {\n  if (isTrashDeleteBlocked(e)) {\n    ids = ids.filter(id => id !== getTrashFolderId());\n    if (ids.length) await Folder.batchDelete(ids, opts);\n    return;\n  }\n  throw e;\n}","preventionTips":["Filter getTrashFolderId() out of any programmatic deletion list.","Use emptyTrash() to clear trash contents, never delete the trash folder.","Exclude system folders (trash, conflict, shared root) in 'select all' UI flows.","Audit plugins/scripts that enumerate folders for deletion."],"tags":["folder","trash","data-protection","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}