sgl-project/sglang · error · ValueError

QwenImage text conditioning mask has shape {tuple(mask.shape

Error message

QwenImage text conditioning mask has shape {tuple(mask.shape)}, expected {(batch_size, text_seq_len)}.

What it means

For text conditioning, QwenImage expects a per-encoder boolean mask of shape (batch_size, text_seq_len) matching the current batch. If the stored prompt (or negative prompt) embeds mask has a different shape — stale cache, mismatched encoder index, or reshaped batch — this ValueError is thrown in _prepare_encoder_hidden_states_mask.

Source

Thrown at python/sglang/multimodal_gen/configs/pipeline_configs/qwen_image.py:438

        dim], so we pass a [batch, text_seq_len] boolean mask to keep attention
        on real text tokens and ignore padding.

        If every request uses the full padded length, no mask is needed and this
        returns None. Otherwise, prefer the embedding-aligned mask stored by the
        text encoding stage. If that is unavailable, rebuild the same mask from
        `txt_seq_lens`: position j is valid for row i when
        `j < txt_seq_lens[i]`.
        """
        if all(seq_len == text_seq_len for seq_len in txt_seq_lens):
            return None

        masks_by_encoder = (
            batch.negative_prompt_embeds_mask if negative else batch.prompt_embeds_mask
        )
        if masks_by_encoder is not None and encoder_index < len(masks_by_encoder):
            mask = masks_by_encoder[encoder_index]
            if mask.shape != (batch_size, text_seq_len):
                raise ValueError(
                    "QwenImage text conditioning mask has shape "
                    f"{tuple(mask.shape)}, expected {(batch_size, text_seq_len)}."
                )
            return mask

        # TODO: cache positions by (device, text_seq_len) if this allocation shows up hot.
        positions = torch.arange(text_seq_len, device=batch.prompt_embeds[0].device)
        seq_lens = torch.tensor(
            txt_seq_lens,
            device=batch.prompt_embeds[0].device,
            dtype=torch.long,
        )
        return positions.unsqueeze(0) < seq_lens.unsqueeze(1)

    def prepare_pos_cond_kwargs(self, batch, device, rotary_emb, dtype):
        return self._prepare_cond_kwargs(
            batch, batch.prompt_embeds, rotary_emb, device, dtype, negative=False
        )

View on GitHub (pinned to 0132848349)

Solutions

  1. Recompute text embeddings/masks for the current batch instead of reusing cached ones
  2. Ensure prompt and negative prompt go through the same tokenizer/padding so masks share (batch_size, text_seq_len)
  3. If caching, key the cache by (batch_size, text_seq_len) and invalidate on mismatch

Example fix

# before
mask = cached_masks[encoder_index]  # from earlier request

# after
mask = encode_text(batch)  # recompute for current batch_size / text_seq_len
assert mask.prompt_embeds_mask[0].shape == (batch_size, text_seq_len)
Defensive patterns

Strategy: validation

Validate before calling

expected = (batch_size, text_seq_len)
for enc_idx in range(num_encoders):
    m = masks[enc_idx]
    assert m is None or tuple(m.shape) == expected, f"mask {enc_idx}: {tuple(m.shape)} != {expected}"

Try / catch

try:
    mask = pipe._prepare_encoder_hidden_states_mask(batch, encoder_index, negative, batch_size, text_seq_len)
except ValueError:
    batch.prompt_embeds_mask = None  # drop stale cache
    batch = pipe.encode_text(batch)  # recompute
    mask = pipe._prepare_encoder_hidden_states_mask(batch, encoder_index, negative, batch_size, text_seq_len)

Prevention

When it happens

Trigger: Batch size or text sequence length changed between embedding computation and denoising (e.g. CFG negative branch with different prompt lengths); reusing cached prompt_embeds_mask from a previous request with a different batch/seq len; encoder_index pointing at a mask computed with different padding.

Common situations: Mixing cached text embeddings across requests with different batch sizes or prompt token counts; padding/tokenization inconsistencies between prompt and negative prompt paths; dynamic batching that changes batch_size after masks were computed.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/d5363d96557600ab. Report an issue: GitHub.