Finding
The transformer crate implements head transposition, head merging, and attention-mask validation twice. The general Qwen/GQA path owns checked, Result-returning helpers and rejects a mask whose length differs from the sequence. The ModernBERT path carries a comment that its private helpers are “same as in attention.rs,” but its copies silently leave zero-filled output when a source range is missing and treat missing mask entries as padding.
One transformer representation therefore has two implementations with different failure semantics.
Verified against main e43777cd7f86674197d94e33533147dddfac4c82.
Evidence
Why this matters
Shape and mask disagreements are model-integrity failures. Silent zero filling or implicit padding produces numerically valid tensors over the wrong inputs, which is more difficult to detect than a load/shape error. It can make the ModernBERT CPU path disagree with the shared transformer path while both appear successful, undermining its role as a correctness reference for later GPU work.
The copies also make every safety repair non-transitive. Tightening the checked helper or mask contract in attention.rs does not protect ModernBERT, as the current divergence already demonstrates.
Desired correction
Give the transformer crate one internal owner for:
[seq, heads, d] ↔ [heads, seq, d] materialization;
- exact input/output length validation; and
- sequence-mask length validation.
Have both Qwen/GQA and ModernBERT consume that owner. Keep ModernBERT-specific local-window/global-attention masking as the legitimate delta, but require a validated [seq] mask before applying it.
Replace unwrap_or_default and optional-copy fallthrough in the forward path with typed Error::Shape propagation.
Done when:
- only one production implementation transposes and merges transformer heads;
- both attention families reject malformed tensor and mask lengths through the same error contract;
- ModernBERT cannot return
Ok with zero-filled rows caused by missing slices;
- local/global-window policy remains explicit and separately tested; and
- adversarial fixtures prove a short mask and a malformed QKV/layout buffer fail loudly in both paths.
Finding
The transformer crate implements head transposition, head merging, and attention-mask validation twice. The general Qwen/GQA path owns checked,
Result-returning helpers and rejects a mask whose length differs from the sequence. The ModernBERT path carries a comment that its private helpers are “same as in attention.rs,” but its copies silently leave zero-filled output when a source range is missing and treat missing mask entries as padding.One transformer representation therefore has two implementations with different failure semantics.
Verified against
maine43777cd7f86674197d94e33533147dddfac4c82.Evidence
crates/transformers/src/attention.rs:128-165validatesx,mask.len() == seq, RoPE sequence capacity, and RoPE head width before attention proceeds.attention.rs:318-366definestranspose_seq_headsandmerge_headsasResult<Vec<f32>>; every source and destination slice is checked and a malformed range becomesError::Shape.crates/transformers/src/modernbert.rs:345-369validates onlyx.len() == seq * hidden. It does not validatemask.len() == seqbefore using the mask.modernbert.rs:381-393splits Q/K/V withget(...).unwrap_or_default(), so an internal projection-length disagreement silently appends empty rows rather than producing the documented shape error.modernbert.rs:477-505applies the mask withmask.get(j).copied().unwrap_or(0), converting every missing entry into padding instead of rejecting a malformed mask.modernbert.rs:510-543labels its private head helpers “same as in attention.rs,” but returns plainVec<f32>and copies only when both optional slices exist. A missing range leaves the pre-zeroed destination unchanged and the forward pass still returnsOk.Why this matters
Shape and mask disagreements are model-integrity failures. Silent zero filling or implicit padding produces numerically valid tensors over the wrong inputs, which is more difficult to detect than a load/shape error. It can make the ModernBERT CPU path disagree with the shared transformer path while both appear successful, undermining its role as a correctness reference for later GPU work.
The copies also make every safety repair non-transitive. Tightening the checked helper or mask contract in
attention.rsdoes not protect ModernBERT, as the current divergence already demonstrates.Desired correction
Give the transformer crate one internal owner for:
[seq, heads, d] ↔ [heads, seq, d]materialization;Have both Qwen/GQA and ModernBERT consume that owner. Keep ModernBERT-specific local-window/global-attention masking as the legitimate delta, but require a validated
[seq]mask before applying it.Replace
unwrap_or_defaultand optional-copy fallthrough in the forward path with typedError::Shapepropagation.Done when:
Okwith zero-filled rows caused by missing slices;