Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Non-hub varlen attention backends mispack keys for non-contiguous masks (missed in #14114/#14115) #14139

Description

@qflen

The three non-hub varlen backends pack keys/values by slicing key[b, :valid_len], where valid_len = attn_mask.sum(dim=1) is only the count of valid keys. This assumes the valid keys are contiguous from index 0. For a left-padded or interior-gap mask, this packs the wrong keys: attention runs over padding positions and drops real keys, producing silently wrong output with no error.

The hub variants were fixed for exactly this in #14114 / #14115 (and #13999 / #14102), but the three non-hub backends were not updated.

Affected (src/diffusers/models/attention_dispatch.py):

  • _flash_varlen_attention (flash_varlen) at L2872
  • _flash_varlen_attention_3 (_flash_varlen_3) at L3124
  • _sage_varlen_attention (sage_varlen) at L3757

Correct behavior: gather by the mask's true indices, as the hub variants already do (L2789): indices_k = attn_mask.flatten().nonzero().flatten(), then index the flattened key/value.

Packing divergence (CPU, no kernels needed):

import torch
# joint mask [valid_text, PAD, image...], an interior gap (as QwenImage builds):
attn_mask = torch.tensor([[True, True, False, True, True]])   # 2 text + 1 pad + 2 image
valid_len = attn_mask.sum(1)                                  # tensor([4]), count only

wrong   = torch.arange(5)[: valid_len.item()]                # [0,1,2,3]: keeps PAD@2, drops key@4
correct = attn_mask.flatten().nonzero().flatten()            # [0,1,3,4]

Real trigger:
QwenImage builds a joint mask cat([encoder_hidden_states_mask, image_mask]) (transformer_qwenimage.py:508); with right-padded text and an all-ones image block, the mask has an interior gap. Users who select a varlen backend (e.g. via set_attention_backend) with batched, differing-length prompts get corrupted output. The default backend isn't varlen, so it is opt-in but silent.

Related:
#13809 fixes only the hub FA3 varlen path. #12870 adds separate *_split backends and does not change this packing, so this fix is independent.


I can send PR for this. The three backends diverged because the varlen packing is duplicated inline with no single source of truth, so the hub fix never reached them. I'll fix the non-hub backends, lift the shared packing into one helper (scoped to non-hub, to stay clear of #12870) so it cannot recur, and add a CPU regression matrix across backends and mask shapes.

Let me know ~

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmodels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions