tracel-ai/burn · error

{scheme:?} does not take a per-tensor scale

Error message

{scheme:?} does not take a per-tensor scale

What it means

The mirror case of the previous error: when building quantization parameters, the given scheme does not accept a per-tensor scale (blockwise/two-level schemes carry their own scales inside the encoded bytes), but one was supplied. The API refuses the inconsistent combination with a panic because the extra scale cannot be represented for that scheme.

Source

Thrown at crates/burn-std/src/tensor/quantization.rs:259

        };
        let scale_bytes = encode_scales(scales, scheme.scale_dtype());
        bytes.extend_from_byte_slice_aligned(scale_bytes.as_slice(), QPARAM_ALIGN);

        // Last, so a reader can peel it off the end before the block scales it normalizes.
        match (global_scale_dtype(&scheme), global) {
            (Some(dtype), Some(global)) => {
                // Encoding the per-tensor scale narrower would round it, and the block scales were
                // normalized against the unrounded one.
                assert_eq!(
                    dtype,
                    ScaleDtype::F32,
                    "a two-level scheme stores its per-tensor scale as f32, got {scheme:?}"
                );
                let global_bytes = encode_scales(&[global], dtype);
                bytes.extend_from_byte_slice_aligned(global_bytes.as_slice(), QPARAM_ALIGN);
            }
            (Some(_), None) => panic!("{scheme:?} requires a per-tensor scale"),
            (None, Some(_)) => panic!("{scheme:?} does not take a per-tensor scale"),
            (None, None) => {}
        }

        Self {
            bytes,
            scheme,
            shape,
        }
    }

    /// The number of quantized elements.
    pub fn num_elements(&self) -> usize {
        self.shape.num_elements()
    }

    /// Returns the int8 quantized values with the quantization parameters.
    pub fn into_vec_i8(self) -> (Vec<i8>, DecodedScales) {
        let scheme = self.scheme;

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Pass `None` for the per-tensor scale when the scheme is blockwise/two-level.
  2. Choose the per-tensor scheme variant if you genuinely have (and need) a single global f32 scale.
  3. Strip the global scale before serialization, or use burn's high-level quantize API to encode scales in the correct layout.

Example fix

// before
let params = QuantizationParameters::from_bytes(bytes, blockwise_scheme, Some(scale)); // panics
// after
let params = QuantizationParameters::from_bytes(bytes, blockwise_scheme, None);
Defensive patterns

Strategy: validation

Validate before calling

fn validate_qparams(scheme: QuantizationScheme, scale: Option<f32>) -> Result<(), String> {
    if scheme.is_blockwise() && scale.is_some() {
        return Err(format!("{scheme:?} does not take a per-tensor scale"));
    }
    Ok(())
}

Try / catch

// panic-based constructor; validate arguments first
let result = std::panic::catch_unwind(|| QuantizationParameters::from_bytes(bytes, scheme, scale));

Prevention

When it happens

Trigger: Passing `Some(scale)` as the per-tensor scale to `QParams`/qparams `new` while `scheme` is a blockwise/two-level scheme (e.g. QBlockwise variants) that stores scales per-block in the byte payload.

Common situations: Reusing param-construction code written for per-tensor schemes with a blockwise scheme; exporting a model quantized blockwise and also attaching a leftover global scale; misreading scheme enums when switching between symmetric per-tensor and blockwise quantization.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/ad8c49b0e4597335. Report an issue: GitHub.