{"record":{"id":"9f37b8446f0d551b","repo":"Comfy-Org/ComfyUI","slug":"custom-alpha-video-frame-count-mask-shape-0-d","errorCode":null,"errorMessage":"Custom alpha video frame count ({mask.shape[0]}) does not match the source video frame count ({source_frame_count}). The Beeble API requires one mask per source frame.","messagePattern":"Custom alpha video frame count \\((.+?)\\) does not match the source video frame count \\((.+?)\\)\\. The Beeble API requires one mask per source frame\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_beeble.py","lineNumber":78,"sourceCode":"\nasync def _upload_mask_batch_as_video(\n    cls: type[IO.ComfyNode],\n    mask: Input.Image,\n    *,\n    frame_rate: Fraction,\n    source_frame_count: int,\n    wait_label: str,\n) -> str:\n    \"\"\"Encode a MASK batch (N, H, W) as a grayscale H.264 MP4 at frame_rate and upload.\n\n    The matte is always downscaled to the pixel budget so it stays within Beeble's limit and\n    keeps the same dimensions as the (similarly downscaled) source — both use the same algorithm\n    from the same starting dimensions, and downscaling is a no-op when already within budget.\n    \"\"\"\n    if mask.dim() == 2:\n        mask = mask.unsqueeze(0)\n    if mask.shape[0] != source_frame_count:\n        raise ValueError(\n            f\"Custom alpha video frame count ({mask.shape[0]}) does not match the \"\n            f\"source video frame count ({source_frame_count}). The Beeble API requires \"\n            \"one mask per source frame.\"\n        )\n    images = downscale_image_tensor(convert_mask_to_image(mask), _MAX_PIXELS)\n    alpha_video = InputImpl.VideoFromComponents(Types.VideoComponents(images=images, audio=None, frame_rate=frame_rate))\n    return await upload_video_to_comfyapi(cls, alpha_video, wait_label=wait_label)\n\n\ndef _alpha_mode_input(*, video: bool) -> IO.DynamicCombo.Input:\n    \"\"\"Build the alpha_mode DynamicCombo with mode-specific extra inputs.\"\"\"\n    select_keyframe_tooltip = (\n        \"First-frame keyframe mask. Beeble propagates this across the video.\" if video else \"Grayscale keyframe mask.\"\n    )\n    custom_tooltip = (\n        \"Per-frame grayscale mask covering the entire video. \"\n        \"Must have the same frame count as the source. \"\n        \"Connect a MASK output from SAM3_TrackToMask or similar.\"","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_beeble.py#L60-L96","documentation":"In custom alpha mode Beeble needs one mask frame per source video frame. The node encodes the MASK batch as a grayscale H.264 MP4 and validates that mask.shape[0] equals the source video's frame count before upload; a mismatch would desynchronize the matte from the video.","triggerScenarios":"Calling the Beeble node with alpha_mode='custom' where the connected MASK batch (N,H,W after unsqueeze for 2D) has N != source video frame count — e.g. mask generated from a different clip, a still mask of 1 frame, or a trimmed video.","commonSituations":"Reusing a matte generated for another video; source video trimmed/keyframed after the mask was made; single-frame mask connected to a multi-frame video.","solutions":["Regenerate the mask from the exact source video so frame counts match.","Repeat/interpolate the mask to the source frame count: mask = mask.repeat(source_frame_count, 1, 1) for a constant matte.","Re-check the source video's frame count after any trimming or frame-rate change and re-export the matte."],"exampleFix":"# before\nalpha = single_frame_mask          # shape (1, H, W), video has 120 frames -> ValueError\n\n# after\nalpha = single_frame_mask.repeat(source_frame_count, 1, 1)  # constant matte for all frames","handlingStrategy":"validation","validationCode":"if mask.dim() == 2:\n    mask = mask.unsqueeze(0)\nassert mask.shape[0] == source_frame_count, (\n    f\"mask frames {mask.shape[0]} != source {source_frame_count}\"\n)","typeGuard":"def mask_matches_video(mask, source_frame_count: int) -> bool:\n    m = mask if mask.dim() == 3 else mask.unsqueeze(0)\n    return m.shape[0] == source_frame_count","tryCatchPattern":"try:\n    out = await beeble_node(alpha_mode=\"custom\", custom_alpha=mask)\nexcept ValueError as e:\n    if \"frame count\" in str(e):\n        mask = mask.repeat(source_frame_count, 1, 1)\n        out = await beeble_node(alpha_mode=\"custom\", custom_alpha=mask)","preventionTips":["Generate mattes from the exact source clip, never a sibling export.","For constant mattes, repeat a single frame to the video length.","Re-verify counts after any trim/fps change."],"tags":["beeble","mask","alpha","video","frame-count","input-validation"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}