{"record":{"id":"df7a5476b85574b3","repo":"beekeeper-studio/beekeeper-studio","slug":"cannot-delete-folder-this-name-move-or-remo-df7a54","errorCode":null,"errorMessage":"Cannot delete folder \"${this.name}\" — move or remove its ${pluralize('query', count, true)} first.","messagePattern":"Cannot delete folder \"(.+?)\" — move or remove its (.+?) first\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/studio/src/common/appdb/models/QueryFolder.ts","lineNumber":41,"sourceCode":"  @Column({ type: 'integer', nullable: true, default: null })\n  @PreventMovingFolderInsideItself\n  parentId: Nullable<number> = null\n\n  // Do NOT initialize this to null. A null initializer becomes an own property\n  // that gets copied into transport objects by cls.merge(), and TypeORM treats an\n  // explicitly-null relation as \"unset this FK\", overriding the parentId column.\n  @ManyToOne(() => QueryFolder, { nullable: true, onDelete: 'SET NULL' })\n  @JoinColumn({ name: 'parentId' })\n  parent?: QueryFolder\n\n  @OneToMany(() => FavoriteQuery, (query) => query.queryFolder)\n  queries: FavoriteQuery[]\n\n  @BeforeRemove()\n  async preventRemoveIfNotEmpty(): Promise<void> {\n    const count = await FavoriteQuery.countBy({ queryFolderId: this.id })\n    if (count > 0) {\n      throw new Error(`Cannot delete folder \"${this.name}\" — move or remove its ${pluralize('query', count, true)} first.`)\n    }\n  }\n\n  @BeforeInsert()\n  @BeforeUpdate()\n  async preventDuplicateName(): Promise<void> {\n    if (!this.name) return\n    const where: any = {\n      name: this.name,\n      parentId: this.parentId ?? IsNull(),\n    }\n    if (this.id) where.id = Not(this.id)\n    const existing = await QueryFolder.findOneBy(where)\n    if (existing) {\n      throw new Error(`A folder named \"${this.name}\" already exists in this location.`)\n    }\n  }\n}","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/beekeeper-studio/beekeeper-studio/blob/4e3e03e3223b2b71a1af8926d455f533b80af837/apps/studio/src/common/appdb/models/QueryFolder.ts#L23-L59","documentation":"QueryFolder.preventRemoveIfNotEmpty is a TypeORM @BeforeRemove() hook that blocks deleting a query folder that still contains favorite queries. It counts FavoriteQuery rows with the folder's queryFolderId and throws when the count is greater than zero, forcing the user to move or delete the contained queries first.","triggerScenarios":"Calling folder.remove() / repository.remove(folder) / soft cascade deletes on a QueryFolder whose id is referenced by one or more FavoriteQuery.queryFolderId rows.","commonSituations":"Users trying to delete a folder via sidebar context menu while it still holds saved queries; cleanup scripts deleting folder rows without inspecting children; code assuming cascade delete that is not configured on the relation.","solutions":["Move the contained queries to another folder (update FavoriteQuery.queryFolderId) before removing the folder.","Delete the FavoriteQuery rows in the folder first, then delete the folder.","Catch the error in the UI and show the message so the user can empty the folder manually."],"exampleFix":"// before\nawait queryFolderRepo.remove(folder);\n// after\nconst count = await FavoriteQuery.countBy({ queryFolderId: folder.id });\nif (count === 0) {\n  await queryFolderRepo.remove(folder);\n} else {\n  await FavoriteQuery.update({ queryFolderId: folder.id }, { queryFolderId: otherFolder.id });\n  await queryFolderRepo.remove(folder);\n}","handlingStrategy":"validation","validationCode":"const queryCount = await FavoriteQuery.countBy({ queryFolderId: folder.id });\nif (queryCount > 0) {\n  throw new Error(`Move or remove ${queryCount} quer${queryCount === 1 ? 'y' : 'ies'} before deleting this folder.`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await queryFolderRepo.remove(folder);\n} catch (e) {\n  if (String(e.message).startsWith('Cannot delete folder')) {\n    showError(e.message); // prompt user to empty the folder first\n  }\n}","preventionTips":["Count child FavoriteQuery rows before every folder delete.","Move children to a target folder as part of the delete flow when the user confirms.","Disable the delete action in the UI while the folder is non-empty."],"tags":["typeorm","entity-hook","referential-integrity"],"backgroundTag":"folder-not-empty","analyzedSha":"4e3e03e3223b2b71a1af8926d455f533b80af837","analyzedAt":"2026-08-31T23:21:23.379Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}