{"record":{"id":"d3d8fd904ccac8cb","repo":"invoke-ai/InvokeAI","slug":"each-batch-data-must-have-unique-node-id-and-field","errorCode":null,"errorMessage":"Each batch data must have unique node_id and field_name","messagePattern":"Each batch data must have unique node_id and field_name","errorType":"validation","errorClass":"BatchDuplicateNodeFieldError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/session_queue/session_queue_common.py","lineNumber":135,"sourceCode":"                    continue\n\n                # Get the type of the first item in the list\n                first_item_type = type(datum.items[0])\n                for item in datum.items:\n                    if type(item) is not first_item_type:\n                        raise BatchItemsTypeError(\"All items in a batch must have the same type\")\n        return v\n\n    @field_validator(\"data\")\n    def validate_unique_field_mappings(cls, v: Optional[BatchDataCollection]):\n        if v is None:\n            return v\n        paths: set[tuple[str, str]] = set()\n        for batch_data_list in v:\n            for datum in batch_data_list:\n                pair = (datum.node_path, datum.field_name)\n                if pair in paths:\n                    raise BatchDuplicateNodeFieldError(\"Each batch data must have unique node_id and field_name\")\n                paths.add(pair)\n        return v\n\n    @model_validator(mode=\"after\")\n    def validate_batch_nodes_and_edges(self):\n        if self.data is None:\n            return self\n        for batch_data_list in self.data:\n            for batch_data in batch_data_list:\n                try:\n                    node = self.graph.get_node(batch_data.node_path)\n                except NodeNotFoundError:\n                    raise NodeNotFoundError(f\"Node {batch_data.node_path} not found in graph\")\n                if batch_data.field_name not in type(node).model_fields:\n                    raise NodeNotFoundError(f\"Field {batch_data.field_name} not found in node {batch_data.node_path}\")\n        return self\n\n    @field_validator(\"graph\")","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/session_queue/session_queue_common.py#L117-L153","documentation":"Each batch datum targets one (node_path, field_name) pair; duplicate pairs would make the batch expansion ambiguous. The BatchDuplicateNodeFieldError is raised by validate_unique_field_mappings when the same node field appears more than once in the batch data.","triggerScenarios":"Adding the same node's same field twice to batch data (e.g. two collections both bound to node 'n.prompt'), often via programmatic batch construction or duplicated JSON entries.","commonSituations":"Copy-pasting a batch data entry and forgetting to change the node_path or field_name; scripts that loop and append the same mapping repeatedly; merging batch configs from two sources.","solutions":["Remove the duplicate batch data entry, keeping one per (node_path, field_name) pair","Deduplicate with paths = set((d.node_path, d.field_name) for ...) in code that builds batch data","If two collections must feed the same field, merge their items into one collection instead","Review hand-edited batch JSON for duplicated keys"],"exampleFix":"// before\n{\"data\": [[{node_path: \"n\", field_name: \"prompt\", ...}], [{node_path: \"n\", field_name: \"prompt\", ...}]]}\n// after: merge into one datum\n{\"data\": [[{node_path: \"n\", field_name: \"prompt\", items: [...all items...]}]]}","handlingStrategy":"validation","validationCode":"def validate_unique_pairs(data):\n    seen = set()\n    for batch_data_list in data:\n        for d in batch_data_list:\n            pair = (d.node_path, d.field_name)\n            if pair in seen:\n                raise ValueError(f\"Duplicate batch binding: {pair}\")\n            seen.add(pair)","typeGuard":"def all_unique(data) -> bool:\n    pairs = [(d.node_path, d.field_name) for lst in data for d in lst]\n    return len(pairs) == len(set(pairs))","tryCatchPattern":"try:\n    batch = Batch(**batch_dict)\nexcept BatchDuplicateNodeFieldError as e:\n    logger.error(f\"Duplicate node/field binding: {e}\")\n    # deduplicate data and rebuild","preventionTips":["Build batch data through a keyed dict {(node_path, field_name): datum} to prevent duplicates","After merging batch configs, deduplicate on (node_path, field_name)","Never copy-paste batch data entries without changing the target","Validate pairs before enqueue"],"tags":["batch","validation","duplicate"],"backgroundTag":"batch-duplicate-node-field","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}