{"record":{"id":"840697ef616a99c9","repo":"invoke-ai/InvokeAI","slug":"not-authorized-to-delete-this-board","errorCode":null,"errorMessage":"Not authorized to delete this board","messagePattern":"Not authorized to delete this board","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"warning","filePath":"invokeai/app/api/routers/boards.py","lineNumber":136,"sourceCode":"        raise HTTPException(status_code=500, detail=\"Failed to update board\")\n\n\n@boards_router.delete(\"/{board_id}\", operation_id=\"delete_board\", response_model=DeleteBoardResult)\ndef delete_board(\n    current_user: CurrentUserOrDefault,\n    board_id: str = Path(description=\"The id of board to delete\"),\n    include_images: Optional[bool] = Query(\n        description=\"Permanently delete all images and videos on the board\", default=False\n    ),\n) -> DeleteBoardResult:\n    \"\"\"Deletes a board (user must have access to it)\"\"\"\n    try:\n        board = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)\n    except Exception:\n        raise HTTPException(status_code=404, detail=\"Board not found\")\n\n    if not current_user.is_admin and board.user_id != current_user.user_id:\n        raise HTTPException(status_code=403, detail=\"Not authorized to delete this board\")\n\n    # Admins delete everything on the board; regular owners only delete their own\n    # contributions so that contributions from other users to a public/shared board\n    # are preserved (they cascade to \"uncategorized\" via FK on board_videos / board_images).\n    cascade_user_id: Optional[str] = None if current_user.is_admin else current_user.user_id\n    deleted_images: list[str] = []\n    deleted_videos: list[str] = []\n\n    try:\n        if include_images is True:\n            assert_image_move_maintenance_inactive()\n            # The services report both outcomes: records whose file delete failed are\n            # preserved (they cascade to \"uncategorized\" via the board FKs when the\n            # board is deleted below) and returned as failures. This is the ground\n            # truth — reconstructing failures by diffing a router-side board listing\n            # against the deleted names would double the DB work and misreport items\n            # moved or deleted concurrently between the two queries.\n            deleted_images, failed_images = ApiDependencies.invoker.services.images.delete_images_on_board(","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/boards.py#L118-L154","documentation":"HTTP 403 raised by the delete_board endpoint when the authenticated user is neither an admin nor the owner of the board (board.user_id != current_user.user_id). The InvokeAI API enforces ownership on destructive board operations; only admins or the creating user may delete a board.","triggerScenarios":"Calling DELETE on a board whose DTO's user_id differs from the JWT/session user's id while current_user.is_admin is false.","commonSituations":"Two users on the same InvokeAI instance sharing boards; a stale token or admin-downgraded account operating on another user's board; automated scripts reusing credentials of a non-owner user.","solutions":["Delete the board as the owning user or an admin account","Verify current_user.user_id matches board.user_id (fetch the board DTO first to check ownership)","If the board should be shared-deletable, promote the account to admin or transfer ownership of the board"],"exampleFix":"// before: deleting as another user\ndeleteBoard(boardId); // 403 Not authorized\n// after: check ownership client-side before calling\nconst board = await getBoard(boardId);\nif (board.user_id === currentUser.user_id || currentUser.is_admin) {\n  await deleteBoard(boardId);\n}","handlingStrategy":"validation","validationCode":"const board = await getBoard(boardId);\nif (!(currentUser.is_admin || board.user_id === currentUser.user_id)) {\n  throw new Error('Skipping delete: not the board owner');\n}","typeGuard":"const canDeleteBoard = (board, user) =>\n  Boolean(user?.is_admin) || board?.user_id === user?.user_id;","tryCatchPattern":"try {\n  await api.delete(`/boards/${boardId}`);\n} catch (e) {\n  if (e.response?.status === 403) notify('Only the board owner or an admin can delete this board');\n  else throw e;\n}","preventionTips":["Check board.user_id against the session user before any destructive board call","Use an admin account for cross-user board maintenance","Surface ownership info in the UI to disable the delete button for non-owners"],"tags":["http-403","authorization","boards","ownership"],"backgroundTag":"insufficient-permissions","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}