{"record":{"id":"6f1c97f1a5d87e03","repo":"invoke-ai/InvokeAI","slug":"board-not-found-6f1c97","errorCode":null,"errorMessage":"Board not found","messagePattern":"Board not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"invokeai/app/api/routers/board_images.py","lineNumber":39,"sourceCode":"    - The user is an admin.\n    - The user owns the board.\n    - The board visibility is Public (public boards accept contributions from any user).\n\n    Reads the board *record*, not its DTO. The decision needs only the owner and the\n    visibility, while BoardService.get_dto also resolves the cover image and runs three COUNT\n    aggregates over the board's contents — six queries to answer a question two columns settle.\n    That cost is the only reason a batch route would be tempted to decide once and reuse the\n    answer for every name, and reusing it is what lets a permission revoked mid-batch keep\n    working until the request ends. One indexed SELECT per name is cheap enough to re-decide.\n    (These routes are sync `def`, so the queries occupy a threadpool worker rather than the\n    event loop — but a 1000-name batch still holds one for six thousand round trips.)\n    \"\"\"\n    from invokeai.app.services.board_records.board_records_common import BoardVisibility\n\n    try:\n        board = ApiDependencies.invoker.services.board_records.get(board_id)\n    except BoardRecordNotFoundException:\n        raise HTTPException(status_code=404, detail=\"Board not found\")\n    # Anything else — a locked or unreadable database — propagates. Catching it here would\n    # answer \"no such board\", which the batch loops below treat as a name to skip: a disk error\n    # would then drop names out of the response entirely, reported neither as moved nor as\n    # failed, and the client would show the move as done until the next refresh.\n    if current_user.is_admin:\n        return\n    if board.user_id == current_user.user_id:\n        return\n    if board.board_visibility == BoardVisibility.Public:\n        return\n    raise HTTPException(status_code=403, detail=\"Not authorized to modify this board\")\n\n\ndef _image_record_exists(image_name: str) -> bool:\n    \"\"\"True if the image record is still present, False if it has been deleted.\n\n    A storage error answers True: only a record positively known to be gone may be downgraded\n    from a reported failure to a silent skip. `ImageRecordStorage.get` no longer translates","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/board_images.py#L21-L57","documentation":"_assert_board_write_access looks up the board via board_records.get(board_id) and converts BoardRecordNotFoundException into HTTP 404 'Board not found'. It is shared by add_image_to_board, remove_image_from_board, add_images_to_board and remove_images_from_board, so any board mutation against a nonexistent board id fails with this error before any writes happen.","triggerScenarios":"POST/DELETE board image endpoints with a board_id that does not exist (deleted board, mistyped id, board on a different InvokeAI instance).","commonSituations":"Stale client cache holding a board that was deleted elsewhere; copied board ids between dev/prod instances; typos in board uuid when scripting against the API.","solutions":["List boards (GET /boards/) and confirm the board_id exists before mutating","Refresh the client board list and retry with a valid board id","If scripting, fetch the id dynamically instead of hardcoding a uuid"],"exampleFix":"// before\nawait api.addImageToBoard({ board_id: 'stale-id', image_name }); // 404\n// after\nconst boards = await api.listBoards();\nconst board = boards.find(b => b.board_name === 'My Board');\nif (board) await api.addImageToBoard({ board_id: board.board_id, image_name });","handlingStrategy":"validation","validationCode":"const boards = await api.listBoards();\nif (!boards.some(b => b.board_id === boardId)) {\n  throw new Error(`Board ${boardId} does not exist`);\n}\nawait api.addImageToBoard({ board_id: boardId, image_name });","typeGuard":"function boardExists(boards: BoardDTO[], boardId: string): boardId is string {\n  return boards.some(b => b.board_id === boardId);\n}","tryCatchPattern":"try {\n  await api.addImageToBoard({ board_id: boardId, image_name });\n} catch (e) {\n  if (e.response?.status === 404 && e.response?.data?.detail === 'Board not found') {\n    await refreshBoardList();\n  } else throw e;\n}","preventionTips":["Refresh board lists after any board deletion on any client","Never hardcode board uuids; resolve them by name at call time","Treat 404 on board mutations as stale-cache and re-sync"],"tags":["http-404","board","not-found","rest-api"],"backgroundTag":"resource-not-found","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}