{"record":{"id":"62fc49cc9d4ea23c","repo":"Comfy-Org/ComfyUI","slug":"connect-at-least-one-keyframe-image","errorCode":null,"errorMessage":"Connect at least one keyframe image.","messagePattern":"Connect at least one keyframe image\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_bfl.py","lineNumber":1327,"sourceCode":"        )\n\n    @classmethod\n    async def execute(\n        cls,\n        prompt: str,\n        keyframes: IO.Autogrow.Type,\n        placement: dict,\n        aspect_ratio: str,\n        duration: str,\n        resolution: str,\n        generate_audio: bool,\n        safety_tolerance: int,\n        seed: int,\n    ) -> IO.NodeOutput:\n        fields = cls.common_fields(prompt, aspect_ratio, duration, resolution, generate_audio, safety_tolerance)\n        images = _flux3_collect_images(keyframes, \"keyframes\")\n        if not images:\n            raise ValueError(\"Connect at least one keyframe image.\")\n        times = None\n        if placement[\"placement\"] == \"at times\":\n            times = _flux3_parse_times(placement[\"times\"], len(images), fields[\"duration\"])\n        elif len(images) >= 3 and fields[\"duration\"] == \"auto\":\n            # spread images land evenly between the first and last, which needs a known length\n            raise ValueError(\n                f\"Spreading {len(images)} images across the clip needs an explicit duration: \"\n                \"set duration, or place the images yourself with 'at times'.\"\n            )\n        urls = await upload_images_to_comfyapi(\n            cls, images, max_images=_FLUX3_MAX_IMAGES, wait_label=\"Uploading keyframes\"\n        )\n        request = Flux3ImageToVideoRequest(\n            keyframes=list(zip(times, urls)) if times is not None else urls,\n            **fields,\n        )\n        return await _flux3_execute(cls, request)\n","sourceCodeStart":1309,"sourceCodeEnd":1345,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_bfl.py#L1309-L1345","documentation":"Thrown by the FLUX.3 image-to-video node when the keyframes Autogrow input contains zero images. _flux3_collect_images walks the Autogrow entries and returns an empty list when nothing is connected, so the node refuses to build the request because an animation needs at least one keyframe.","triggerScenarios":"Executing Flux3Keyframe node with no IMAGE wires attached to any keyframes slot; or all keyframe Autogrow inputs left unconnected while expecting the node to fall back to text-to-video.","commonSituations":"User switches from the text-to-video FLUX.3 node to the keyframe node but forgets to wire images; UI saves a workflow with Autogrow slots the user thought were optional; batch upstream node produces an empty batch that gets filtered out before collection.","solutions":["Connect at least one LoadImage/Image output into the first keyframes Autogrow slot.","If you want text-to-video with no keyframes, use the plain FLUX.3 text-to-video node instead.","Verify the upstream image node actually emits a non-empty batch (check its output preview)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"keyframe_count = sum(1 for entry in keyframes if entry is not None)\nif keyframe_count == 0:\n    raise UserError('Connect at least one keyframe image before queueing.')","typeGuard":"def has_keyframes(keyframes) -> bool:\n    return any(entry is not None and len(entry) > 0 for entry in keyframes)","tryCatchPattern":null,"preventionTips":["Wire a preview/image node downstream of the keyframe source so an empty batch is visible before running.","Treat the keyframe node as image-to-video only; use the text-to-video node when no keyframes are needed."],"tags":["comfyui","flux3","validation","input-required","api-nodes"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}