invoke-ai/InvokeAI · critical · HTTPException

Failed to add image to board

Error message

Failed to add image to board

What it means

add_image_to_board wraps its board-record update in a broad try/except; any exception from the underlying services is converted to HTTP 500 'Failed to add image to board'. Because this is the single-image route, a failure is a hard 500 rather than a partial success recorded in failed_images.

Source

Thrown at invokeai/app/api/routers/board_images.py:172

    _assert_image_direct_owner(image_name, current_user)
    assert_image_move_maintenance_inactive()
    try:
        added_images: set[str] = set()
        affected_boards: set[str] = set()
        old_board_id = ApiDependencies.invoker.services.board_image_records.get_board_for_image(image_name) or "none"
        ApiDependencies.invoker.services.board_images.add_image_to_board(board_id=board_id, image_name=image_name)
        added_images.add(image_name)
        affected_boards.add(board_id)
        affected_boards.add(old_board_id)

        return AddImagesToBoardResult(
            added_images=list(added_images),
            # Single-image route: a failure here is a 500, never a partial success.
            failed_images=[],
            affected_boards=list(affected_boards),
        )
    except Exception:
        raise HTTPException(status_code=500, detail="Failed to add image to board")


@board_images_router.delete(
    "/",
    operation_id="remove_image_from_board",
    responses={
        201: {"description": "The image was removed from the board successfully"},
    },
    status_code=201,
    response_model=RemoveImagesFromBoardResult,
)
def remove_image_from_board(
    current_user: CurrentUserOrDefault,
    image_name: str = Body(description="The name of the image to remove", embed=True),
) -> RemoveImagesFromBoardResult:
    """Removes an image from its board, if it had one"""
    try:
        old_board_id = ApiDependencies.invoker.services.images.get_dto(image_name).board_id or "none"

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Check server logs for the original exception stack trace behind the 500
  2. Verify the board database/storage backend is healthy and writable (the code deliberately lets storage errors propagate from access checks, so a 500 here is likely downstream)
  3. Retry the request; if persistent, restart the service or repair the board records store

Example fix

// before
await api.addImageToBoard({ board_id, image_name }); // opaque 500
// after
try {
  await api.addImageToBoard({ board_id, image_name });
} catch (e) {
  if (e.status === 500) { await retryWithBackoff(() => api.addImageToBoard({ board_id, image_name })); }
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

await assertBoardWriteAccess(boardId); // pre-check board exists and is writable
await assertImageDirectOwner(imageName); // pre-check ownership before the add

Try / catch

try {
  await api.addImageToBoard({ board_id: boardId, image_name });
} catch (e) {
  if (e.response?.status === 500 && e.response?.data?.detail === 'Failed to add image to board') {
    await retryWithBackoff(() => api.addImageToBoard({ board_id: boardId, image_name }), { attempts: 3 });
  } else throw e;
}

Prevention

When it happens

Trigger: Any unexpected exception during the add operation — e.g. board record service failure, database/locked storage error, serialization issue — after access checks passed.

Common situations: Locked or corrupted board database; storage backend (disk/network) errors; bugs in the board records service triggered by edge-case board states.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/98c384da921f5278. Report an issue: GitHub.