{"record":{"id":"963129283ebfcf8c","repo":"Comfy-Org/ComfyUI","slug":"dall-e-2-image-editing-requires-an-image-and-a-mas","errorCode":null,"errorMessage":"Dall-E 2 image editing requires an image AND a mask","messagePattern":"Dall-E 2 image editing requires an image AND a mask","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_openai.py","lineNumber":232,"sourceCode":"            height, width, channels = input_tensor.shape\n            rgba_tensor = torch.ones(height, width, 4, device=\"cpu\")\n            rgba_tensor[:, :, :channels] = input_tensor\n\n            if mask.shape[1:] != image.shape[1:-1]:\n                raise Exception(\"Mask and Image must be the same size\")\n            rgba_tensor[:, :, 3] = 1 - mask.squeeze().cpu()\n\n            rgba_tensor = downscale_image_tensor(rgba_tensor.unsqueeze(0)).squeeze()\n\n            image_np = (rgba_tensor.numpy() * 255).astype(np.uint8)\n            img = Image.fromarray(image_np)\n            img_byte_arr = BytesIO()\n            img.save(img_byte_arr, format=\"PNG\")\n            img_byte_arr.seek(0)\n            img_binary = img_byte_arr  # .getvalue()\n            img_binary.name = \"image.png\"\n        elif image is not None or mask is not None:\n            raise Exception(\"Dall-E 2 image editing requires an image AND a mask\")\n\n        response = await sync_op(\n            cls,\n            ApiEndpoint(path=path, method=\"POST\"),\n            response_model=OpenAIImageGenerationResponse,\n            data=request_class(\n                model=model,\n                prompt=prompt,\n                n=n,\n                size=size,\n                seed=seed,\n            ),\n            files=(\n                {\n                    \"image\": (\"image.png\", img_binary, \"image/png\"),\n                }\n                if img_binary\n                else None","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_openai.py#L214-L250","documentation":"Raised by the Dall-E 2 image-edit node when exactly one of image or mask is provided. Dall-E 2's /images/edits endpoint requires both an input image and a mask, so the XOR branch (image is not None or mask is not None, but not both) throws this Exception before any request is sent.","triggerScenarios":"Calling the edit node with only a mask connected (no image) or only an image connected (no mask) while the Dall-E 2 edit path is selected.","commonSituations":"User switches intent from inpainting to image-variation but leaves one socket empty; workflow refactoring drops one of the two links; assuming gpt-image-1 semantics (mask optional) on the Dall-E 2 model.","solutions":["Connect both an image and a mask to the edit node, or disconnect both for pure generation","Switch to the gpt-image-1/gpt-image-2 path if you want mask-optional editing","Verify both wires survived workflow edits (visual inspection of the node inputs)"],"exampleFix":"// before\nedit(image=my_image, mask=None)   # Dall-E 2: raises\n// after\nedit(image=my_image, mask=my_mask)  # both provided\n// or use the gpt-image model path for maskless editing","handlingStrategy":"validation","validationCode":"if (image is None) != (mask is None):\n    raise ValueError(\"Dall-E 2 edit needs both image and mask, or neither\")","typeGuard":"def valid_dalle2_edit_inputs(image, mask) -> bool:\n    return (image is None) == (mask is None)","tryCatchPattern":null,"preventionTips":["Wire image and mask as a pair - add or remove them together","Prefer the gpt-image path when only an image (no mask) is available","Smoke-test edited workflows after reconnecting inputs"],"tags":["openai","dalle2","mask","image-edit","required-input"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}