laurent22/joplin · error · Error
The trash folder cannot be deleted
Error message
The trash folder cannot be deleted
What it means
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.
Source
Thrown at packages/lib/models/Folder.ts:143
'notes': Note,
'resources': Resource,
};
for (const tableName of ['folders', 'notes', 'resources']) {
const ItemClass = tableNameToClasses[tableName];
const rows = await this.db().selectAll(`SELECT id FROM ${tableName} WHERE share_id = ?`, [shareId]);
const ids: string[] = rows.map(r => r.id);
await ItemClass.batchDelete(ids, deleteOptions);
}
}
public static async batchDelete(folderIds: string[], options: DeleteOptions): Promise<void> {
options = {
deleteChildren: true,
...options,
};
if (folderIds.includes(getTrashFolderId())) throw new Error('The trash folder cannot be deleted');
const toTrash = !!options.toTrash;
const folders: FolderEntity[] = await Folder.loadItemsByIds(folderIds);
if (!folders.length) return; // noop
const actionLogger = ActionLogger.from(options.sourceDescription);
actionLogger.addDescription(`folder titles: ${JSON.stringify(folders.map(folder => folder.title))}`);
options.sourceDescription = actionLogger;
if (options.deleteChildren) {
const childrenDeleteOptions: DeleteOptions = {
disableReadOnlyCheck: options.disableReadOnlyCheck,
sourceDescription: actionLogger,
deleteChildren: true,
toTrash,
};
View on GitHub (pinned to 2654b33620)
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.
Example fix
// before
await Folder.batchDelete(allFolderIds, { toTrash: false });
// after - exclude the trash folder
const deletable = allFolderIds.filter(id => id !== getTrashFolderId());
await Folder.batchDelete(deletable, { toTrash: false }); Defensive patterns
Strategy: validation
Validate before calling
const safeIds = folderIds.filter(id => id !== getTrashFolderId());
if (safeIds.length !== folderIds.length) {
throw new Error('Refusing to delete: trash folder ID was in the list.');
} Type guard
function isTrashDeleteBlocked(e: any): boolean {
return e && typeof e.message === 'string' && e.message === 'The trash folder cannot be deleted';
} Try / catch
try {
await Folder.batchDelete(ids, opts);
} catch (e) {
if (isTrashDeleteBlocked(e)) {
ids = ids.filter(id => id !== getTrashFolderId());
if (ids.length) await Folder.batchDelete(ids, opts);
return;
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot find "%s".
- Cannot move notebook to this location
- Parent ID cannot be the same as ID
- Notebooks cannot be named "%s", which is a reserved title.
- Cannot find "%s".
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/043133e69bde76a2.
Report an issue: GitHub.