{"record":{"id":"07672ce4e6d7fca5","repo":"sgl-project/sglang","slug":"caption-valid-mask-must-have-one-row-per-z-image-c","errorCode":null,"errorMessage":"caption_valid_mask must have one row per Z-Image caption","messagePattern":"caption_valid_mask must have one row per Z-Image caption","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/models/dits/zimage.py","lineNumber":979,"sourceCode":"        caption_valid_mask: torch.Tensor | None = None,\n    ):\n        \"\"\"Patchify images and pad image/caption tokens to batch targets.\n\n        Each image is [C, F, H, W] and has one [L, D] caption. Returned tensors\n        are stacked as [B, S, D], while valid lengths keep track of real tokens\n        before learned pad tokens are restored. `image_seq_len_target`, when\n        set, is the SP-local padded image-token target.\n        \"\"\"\n        if len(all_image) != len(all_cap_feats):\n            raise ValueError(\n                f\"Z-Image expects one caption embedding per image, got {len(all_image)} images and {len(all_cap_feats)} captions\"\n            )\n        if not all_image:\n            raise ValueError(\"Z-Image batch must contain at least one image latent\")\n        if caption_valid_mask is not None and caption_valid_mask.shape[0] != len(\n            all_cap_feats\n        ):\n            raise ValueError(\"caption_valid_mask must have one row per Z-Image caption\")\n\n        pH = pW = patch_size\n        pF = f_patch_size\n        all_image_out = []\n        all_image_size = []\n        all_cap_feats_out = []\n        all_image_valid_lens = []\n        all_cap_valid_lens = []\n        all_cap_valid_masks = []\n        all_image_attn_lens = []\n        all_cap_attn_lens = []\n        image_records = []\n\n        cap_seq_len_target = max(\n            self._ceil_to_multiple(cap_feat.size(0), SEQ_MULTI_OF)\n            for cap_feat in all_cap_feats\n        )\n","sourceCodeStart":961,"sourceCodeEnd":997,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/models/dits/zimage.py#L961-L997","documentation":"Raised by ZImage.patchify_and_embed when an optional caption_valid_mask is provided whose first dimension does not equal len(all_cap_feats). The mask must contain one row (one per-caption validity vector) per caption embedding.","triggerScenarios":"Passing caption_valid_mask with shape [B_total, L] while all_cap_feats was filtered down to fewer captions after the mask was built.","commonSituations":"Building the mask from the pre-filter batch but filtering caption embeddings afterwards; off-by-one errors in batch slicing.","solutions":["Compute caption_valid_mask after filtering, from the same list used for all_cap_feats","Add an assert caption_valid_mask.shape[0] == len(all_cap_feats) in your batching code","Pass caption_valid_mask=None if you don't need per-caption validity"],"exampleFix":"# before\nmask = build_mask(all_caps_before_filter)\nall_cap_feats = [c for c, ok in zip(all_caps_before_filter, oks) if ok]\n# after\nall_cap_feats = [c for c, ok in zip(all_caps_before_filter, oks) if ok]\nmask = build_mask(all_cap_feats)","handlingStrategy":"validation","validationCode":"if caption_valid_mask is not None:\n    assert caption_valid_mask.shape[0] == len(all_cap_feats)","typeGuard":"def mask_matches_captions(mask: torch.Tensor | None, n_caps: int) -> bool:\n    return mask is None or mask.shape[0] == n_caps","tryCatchPattern":null,"preventionTips":["Derive mask from the final filtered caption list, never the pre-filter batch"],"tags":["z-image","mask-validation","batching"],"backgroundTag":"mask-shape-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}