{"record":{"id":"9283d6b50e7755ec","repo":"invoke-ai/InvokeAI","slug":"call-saved-workflow-caller-does-not-have-access-to","errorCode":null,"errorMessage":"call_saved_workflow caller does not have access to board '{board_id}' for image generator expansion","messagePattern":"call_saved_workflow caller does not have access to board '(.+?)' for image generator expansion","errorType":"exception","errorClass":"UnsupportedWorkflowNodeError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/session_processor/workflow_call_batch.py","lineNumber":358,"sourceCode":"            board_visibility = BoardVisibility.Private\n    if board_visibility in {BoardVisibility.Shared, BoardVisibility.Public}:\n        return\n\n    if hasattr(board_records, \"get_all\"):\n        try:\n            accessible_boards = board_records.get_all(\n                user_id=user_id,\n                is_admin=False,\n                order_by=BoardRecordOrderBy.Name,\n                direction=SQLiteDirection.Ascending,\n                include_archived=True,\n            )\n        except Exception:\n            accessible_boards = []\n        if any(getattr(board, \"board_id\", None) == board_id for board in accessible_boards):\n            return\n\n    raise UnsupportedWorkflowNodeError(\n        f\"call_saved_workflow caller does not have access to board '{board_id}' for image generator expansion\"\n    )\n\n\ndef _resolve_image_generator(value: Mapping[str, Any], services: Any, user_id: str | None) -> list[ImageField]:\n    generator_type = value.get(\"type\")\n    if generator_type != \"image_generator_images_from_board\":\n        raise UnsupportedWorkflowNodeError(f\"Unsupported image generator type '{generator_type}'\")\n    board_id = value.get(\"board_id\")\n    if not isinstance(board_id, str) or not board_id:\n        return []\n    _assert_user_can_access_board(board_id, services, user_id)\n    category = value.get(\"category\", \"images\")\n    categories = IMAGE_CATEGORIES if category == \"images\" else ASSETS_CATEGORIES\n    image_names = services.board_images.get_all_board_image_names_for_board(\n        board_id=board_id,\n        categories=categories,\n        is_intermediate=False,","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/session_processor/workflow_call_batch.py#L340-L376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Have the board owner set the board's visibility to Shared or Public","Change the image_generator node to reference a board the calling user owns","Run the call as an admin user or with a user_id that owns the board","Replace the board-based generator with an image_batch node listing the images directly"],"exampleFix":"// before: invoking as user 'bob' a workflow referencing alice's private board\n// after: change board visibility\nPUT /boards/{board_id} { \"board_visibility\": \"Public\" }\n// or point the node at a board owned by 'bob'","handlingStrategy":"try-catch","validationCode":"def can_access_board(services, board_id: str, user_id: str | None) -> bool:\n    if not user_id:\n        return True\n    try:\n        rec = services.board_records.get(board_id)\n    except Exception:\n        return False\n    if getattr(rec, \"user_id\", None) == user_id:\n        return True\n    vis = getattr(rec, \"board_visibility\", None)\n    return vis in {\"shared\", \"public\"}","typeGuard":"from enum import Enum\n\ndef is_shared_or_public(vis: object) -> bool:\n    return vis in {BoardVisibility.Shared, BoardVisibility.Public} or vis in {\"shared\", \"public\"}","tryCatchPattern":"try:\n    sessions = build_batch_child_workflow_sessions(...)\nexcept UnsupportedWorkflowNodeError as e:\n    if \"does not have access to board\" in str(e):\n        board_id = re.search(r\"board '(.+?)'\", str(e)).group(1)\n        # surface to user: request board sharing or pick an owned board\n        raise BoardAccessDenied(board_id, user_id) from e\n    raise","preventionTips":["Only reference boards owned by the calling user, or boards marked Shared/Public","In multi-user deployments, validate board access before invoking saved workflows","Avoid distributing workflows containing other users' private board ids","Re-check board visibility after permission changes"],"tags":["board","workflow","permissions","authorization","invokeai"],"backgroundTag":"permission-denied","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}