invoke-ai/InvokeAI · error · UnsupportedWorkflowNodeError

call_saved_workflow caller does not have access to board '{b

Error message

call_saved_workflow caller does not have access to board '{board_id}' for image generator expansion

What it means

The board referenced by an image_generator (images_from_board) node exists, but the calling user is not allowed to use it in a called batch child workflow. Access is denied when the caller is not an admin, is not the board owner, the board is Private (not Shared/Public), and the board does not appear in the caller's accessible board list. This is an authorization check, not a data error.

Source

Thrown at invokeai/app/services/session_processor/workflow_call_batch.py:358

            board_visibility = BoardVisibility.Private
    if board_visibility in {BoardVisibility.Shared, BoardVisibility.Public}:
        return

    if hasattr(board_records, "get_all"):
        try:
            accessible_boards = board_records.get_all(
                user_id=user_id,
                is_admin=False,
                order_by=BoardRecordOrderBy.Name,
                direction=SQLiteDirection.Ascending,
                include_archived=True,
            )
        except Exception:
            accessible_boards = []
        if any(getattr(board, "board_id", None) == board_id for board in accessible_boards):
            return

    raise UnsupportedWorkflowNodeError(
        f"call_saved_workflow caller does not have access to board '{board_id}' for image generator expansion"
    )


def _resolve_image_generator(value: Mapping[str, Any], services: Any, user_id: str | None) -> list[ImageField]:
    generator_type = value.get("type")
    if generator_type != "image_generator_images_from_board":
        raise UnsupportedWorkflowNodeError(f"Unsupported image generator type '{generator_type}'")
    board_id = value.get("board_id")
    if not isinstance(board_id, str) or not board_id:
        return []
    _assert_user_can_access_board(board_id, services, user_id)
    category = value.get("category", "images")
    categories = IMAGE_CATEGORIES if category == "images" else ASSETS_CATEGORIES
    image_names = services.board_images.get_all_board_image_names_for_board(
        board_id=board_id,
        categories=categories,
        is_intermediate=False,

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Have the board owner set the board's visibility to Shared or Public
  2. Change the image_generator node to reference a board the calling user owns
  3. Run the call as an admin user or with a user_id that owns the board
  4. Replace the board-based generator with an image_batch node listing the images directly

Example fix

// before: invoking as user 'bob' a workflow referencing alice's private board
// after: change board visibility
PUT /boards/{board_id} { "board_visibility": "Public" }
// or point the node at a board owned by 'bob'
Defensive patterns

Strategy: try-catch

Validate before calling

def can_access_board(services, board_id: str, user_id: str | None) -> bool:
    if not user_id:
        return True
    try:
        rec = services.board_records.get(board_id)
    except Exception:
        return False
    if getattr(rec, "user_id", None) == user_id:
        return True
    vis = getattr(rec, "board_visibility", None)
    return vis in {"shared", "public"}

Type guard

from enum import Enum

def is_shared_or_public(vis: object) -> bool:
    return vis in {BoardVisibility.Shared, BoardVisibility.Public} or vis in {"shared", "public"}

Try / catch

try:
    sessions = build_batch_child_workflow_sessions(...)
except UnsupportedWorkflowNodeError as e:
    if "does not have access to board" in str(e):
        board_id = re.search(r"board '(.+?)'", str(e)).group(1)
        # surface to user: request board sharing or pick an owned board
        raise BoardAccessDenied(board_id, user_id) from e
    raise

Prevention

When it happens

Trigger: Calling a saved workflow with an image_generator referencing another user's Private board_id while authenticated as a non-admin user_id; board exists and loads fine but visibility is Private and get_all for the caller doesn't include it.

Common situations: Sharing workflows between users in a multi-user InvokeAI deployment; workflows saved by an admin referencing their private boards, then invoked by regular users; board visibility changed to Private after the workflow was saved.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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