tracel-ai/burn · error

Tensor is on the wrong backend (expected {backend}).

Error message

Tensor is on the wrong backend (expected {backend}).

What it means

The burn-dispatch `unwrap_vec!` macro converts dispatch tensors into backend-specific tensors. When a tensor's DispatchTensorKind variant does not match the backend being unwrapped, the library panics with 'Tensor is on the wrong backend'. This guards against mixing tensors created by different backends in one operation.

Source

Thrown at crates/burn-dispatch/src/macros.rs:396

    ($kind:ident, $inner_fn:ident, $tensor:expr, $device:expr, $to_device:ident, |$inner:ident, $device_ident:ident| $body:expr) => {
        backend_matrix!(
            float_to_device_arms,
            $tensor,
            $device,
            $to_device,
            |$inner, $device_ident| $body
        )
    };
}

/// Unwraps a `Vec<DispatchTensor>` for a known backend.
macro_rules! unwrap_vec {
    ($Backend:ident, $vec:expr, $kind:ident) => {
        $vec.into_iter()
            .map(|t| match t.kind {
                $crate::DispatchTensorKind::$Backend(inner) => inner.$kind(),
                #[allow(unreachable_patterns)]
                _ => panic!(
                    "Tensor is on the wrong backend (expected {}).",
                    stringify!($Backend)
                ),
            })
            .collect::<Vec<_>>()
    };

    // Autodiff-wrapped backend
    (@autodiff $Backend:ident, $vec:expr, $kind:ident) => {
        $vec.into_iter()
            .map(|t| match t.kind {
                $crate::DispatchTensorKind::Autodiff(inner) => match *inner {
                    $crate::DispatchTensorKind::$Backend(inner) => inner.$kind(),
                    _ => panic!(
                        "Autodiff float tensor is on the wrong backend (expected {}).",
                        stringify!($Backend)
                    ),
                },

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Ensure every tensor passed to the operation was created by the same backend as the operation's target backend
  2. Call `.to_device(...)` / recreate the tensor on the target backend before the call
  3. Check that you are not mixing an Autodiff-wrapped tensor with a primitive tensor where the unwrap expects the primitive variant

Example fix

// before
let out = my_op(vec![cpu_tensor]); // running under Wgpu backend
// after
let out = my_op(vec![cpu_tensor.to_device(&wgpu_device)]); // convert to the active backend
Defensive patterns

Strategy: validation

Validate before calling

fn assert_same_backend<B: Backend>(t: &burn::tensor::Tensor<B, D>) -> bool { /* ensure tensor's backend type parameter equals the op's backend */ true } // in Rust this is enforced statically via the B type parameter — keep all tensors as Tensor<B, _> for one B

Prevention

When it happens

Trigger: Calling a dispatch-level tensor API (e.g. via Autodiff backend, ad_enabled, or memory_persistent_allocations paths) passing a vector of tensors where at least one tensor was created on a different backend than the one the macro was instantiated for (e.g. a Cpu tensor given to a Wgpu-dispatched op).

Common situations: Moving tensors between devices/backends and forgetting to convert them; passing a tensor from a non-autodiff backend into an autodiff-wrapped graph; storing tensors across backend switches in cached state.

Related errors


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