{"record":{"id":"989aa4350144be95","repo":"invoke-ai/InvokeAI","slug":"failed-to-remove-images-from-board","errorCode":null,"errorMessage":"Failed to remove images from board","messagePattern":"Failed to remove images from board","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"invokeai/app/api/routers/board_images.py","lineNumber":435,"sourceCode":"                    affected_boards.add(old_board_id)\n                elif outcome is _ScopedRemoveOutcome.MOVED:\n                    failed_images.add(image_name)\n                # GONE: a skip, exactly as the gone-block above treats a name that vanished\n                # before the loop reached it.\n            except Exception:\n                # A genuine storage failure, not an auth/404 skip — see add_images_to_board.\n                # The zero-row classification's own reads land here too: a name whose state\n                # cannot be decided is reported, never dropped.\n                failed_images.add(image_name)\n        return RemoveImagesFromBoardResult(\n            removed_images=list(removed_images),\n            failed_images=list(failed_images),\n            affected_boards=list(affected_boards),\n        )\n    except HTTPException:\n        raise\n    except Exception:\n        raise HTTPException(status_code=500, detail=\"Failed to remove images from board\")\n","sourceCodeStart":417,"sourceCodeEnd":436,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/board_images.py#L417-L436","documentation":"HTTP 500 raised by POST /board_images/batch/delete (remove_images_from_board) on any unexpected exception in the bulk removal path. Expected per-image misses are collected into failed_images; only service/DB level failures reach this generic handler.","triggerScenarios":"POST /board_images/batch/delete with {image_names, board_id} when the board_images service throws — DB failure, concurrent board deletion, or internal service error while dissociating images.","commonSituations":"Bulk cleanup scripts hitting a DB that was concurrently pruned, SQLite 'database is locked' under heavy queue load, or stale board_id from a deleted board.","solutions":["Read the server-side traceback for the root cause","Confirm the board still exists before batch delete","Split large batches into smaller chunks to avoid lock timeouts","Check DB health (locks, connections, disk)","Retry idempotently — already-removed images are reported, not fatal"],"exampleFix":"// before\nawait api.post('/board_images/batch/delete', { image_names: all, board_id });\n// after\nfor (const chunk of chunks(all, 50)) {\n  await api.post('/board_images/batch/delete', { image_names: chunk, board_id });\n}","handlingStrategy":"retry","validationCode":"const board = await api.get(`/boards/${boardId}`).catch(() => null);\nif (!board) throw new Error(`Board ${boardId} missing; skip batch delete`);","typeGuard":"const isServerError = (e) => e?.response?.status >= 500;","tryCatchPattern":"try {\n  await api.post('/board_images/batch/delete', { image_names, board_id });\n} catch (e) {\n  if (isServerError(e)) await sleep(1000 * attempt); // then retry chunk\n  throw e;\n}","preventionTips":["Split large batches into chunks (e.g. 50) to dodge DB lock timeouts","Retry idempotently — already-removed images land in failed_images","Verify the board exists before bulk removal","Check server logs for SQLite 'database is locked' under queue load"],"tags":["http-500","api","batch","database"],"backgroundTag":"unexpected-internal-server-error","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}