tracel-ai/burn · error

scalar_op: unsupported dtype {:?}

Error message

scalar_op: unsupported dtype {:?}

What it means

scalar_op applies an elementwise float op between a tensor and a scalar, dispatching on the tensor dtype; F32, F64, F16 and BF16 are handled (F16/BF16 by converting the scalar through half types) and all other dtypes panic. Used by float_add_scalar and float_sub_scalar, so scalar ops on non-float tensors abort here.

Source

Thrown at crates/burn-flex/src/ops/binary.rs:308

    match dtype {
        DType::F32 => scalar_op_typed(tensor, scalar as f32, f32_op),
        DType::F64 => scalar_op_typed(tensor, scalar, f64_op),
        DType::F16 => {
            let scalar_f16 = f16::from_f32(scalar as f32);
            let s = scalar_f16.to_f32();
            scalar_op_typed(tensor, scalar_f16, |a: f16, _| {
                f16::from_f32(f32_op(a.to_f32(), s))
            })
        }
        DType::BF16 => {
            let scalar_bf16 = bf16::from_f32(scalar as f32);
            let s = scalar_bf16.to_f32();
            scalar_op_typed(tensor, scalar_bf16, |a: bf16, _| {
                bf16::from_f32(f32_op(a.to_f32(), s))
            })
        }
        _ => panic!("scalar_op: unsupported dtype {:?}", dtype),
    }
}

pub(crate) fn scalar_op_typed<E, Op>(mut tensor: FlexTensor, scalar: E, op: Op) -> FlexTensor
where
    E: Element + bytemuck::Pod,
    Op: Fn(E, E) -> E,
{
    // In-place fast path: unique, contiguous at offset 0
    if tensor.is_unique()
        && let Some((0, end)) = tensor.layout().contiguous_offsets()
    {
        let storage: &mut [E] = tensor.storage_mut();
        for x in storage[..end].iter_mut() {
            *x = op(*x, scalar);
        }
        return tensor;
    }

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Cast the tensor to a float dtype first: tensor.cast(DType::F32) then apply the scalar op.
  2. Use int_scalar_op / int_add_scalar instead when the tensor is genuinely integer — that is the integer counterpart.
  3. Keep the pipeline float from the start so scalar adjustments land on float tensors.

Example fix

// before
let y = float_add_scalar(x_i32, 0.5); // panics
// after
let y = float_add_scalar(x_i32.cast(DType::F32), 0.5);
Defensive patterns

Strategy: type-guard

Validate before calling

if !matches!(tensor.dtype(), DType::F32 | DType::F64 | DType::F16 | DType::BF16) {
    tensor = tensor.cast(DType::F32);
}
let y = float_add_scalar(tensor, s);

Type guard

fn is_float_dtype(d: DType) -> bool {
    matches!(d, DType::F32 | DType::F64 | DType::F16 | DType::BF16)
}

Try / catch

let y = std::panic::catch_unwind(|| float_add_scalar(tensor.clone(), s))
    .unwrap_or_else(|_| float_add_scalar(tensor.cast(DType::F32), s));

Prevention

When it happens

Trigger: Calling float_add_scalar/float_sub_scalar (or scalar_op directly, e.g. from tests test_scalar_f16/test_scalar_bf16 variants) on an integer, unsigned or bool tensor. Also occurs when a scalar-only adjustment is applied to a tensor that was cast to int earlier in the pipeline.

Common situations: Adding a bias/offset to a quantized (int8) tensor; test scaffolding reusing int fixtures; expectation of PyTorch-like result_type promotion that burn-flex does not implement.

Related errors


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