tracel-ai/burn · error

{scheme:?} requires a per-tensor scale

Error message

{scheme:?} requires a per-tensor scale

What it means

When constructing quantization parameters (`QuantizationParameters`/qparams `new`) from raw bytes and an optional per-tensor scale, the combination of scheme and scale must be consistent. A scheme that requires a per-tensor scale (e.g. per-tensor affine symmetric schemes without block scaling) was given `Some(...)`-style bytes but no global/per-tensor scale, so construction panics. The qparams would be unusable for dequantization without that scale.

Source

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

            Some(_) => scales,
        };
        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) {

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Provide the per-tensor scale (ScaleDtype::F32) when constructing params for a scheme that requires one.
  2. Use the correct scheme variant that matches the data you actually have (e.g. a blockwise scheme if you have no global scale).
  3. Re-quantize the tensor with burn's quantization API so scales are generated and encoded automatically instead of assembling bytes by hand.

Example fix

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

Strategy: validation

Validate before calling

fn validate_qparams(scheme: QuantizationScheme, scale: Option<f32>) -> Result<(), String> {
    match (scheme, scale) {
        (s, None) if s.requires_per_tensor_scale() => Err(format!("{s:?} requires a per-tensor scale")),
        _ => Ok(()),
    }
}

Try / catch

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

Prevention

When it happens

Trigger: Building quantization params for a scheme such as QAffinePerTensor / symmetric per-tensor modes while passing `None` for the per-tensor scale — e.g. `QParams::new(bytes, scheme, None)` for a scheme whose variant expects `Some(global_scale)`.

Common situations: Hand-writing quantized model export/serialization code and forgetting the scale; converting checkpoints between formats and dropping the scale tensor; copying param construction code from a blockwise (two-level) example and applying it to a per-tensor scheme.

Related errors


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