tracel-ai/burn · critical

Both tensors should be on the same device {:?} != {:?}

Error message

Both tensors should be on the same device {:?} != {:?}

What it means

burn-cubecl panics when two tensors involved in the same operation live on different devices (e.g. one on GPU 0, another on GPU 1, or one on CPU and one on GPU). `assert_is_on_same_device` compares the `device` field of both CubeTensor handles before an operation proceeds, since most kernels cannot read inputs across devices. This is a deliberate fail-fast instead of an implicit (and slow or impossible) cross-device copy.

Source

Thrown at crates/burn-cubecl/src/tensor/base.rs:306

        impl NumericUnaryOpFamily for Copy {
            type Options = ();
            type Unary<T: Numeric, N: Size> = Self;
        }

        let tensor = self.clone();
        launch_unary_numeric::<Copy, _>(tensor, |_| ())
    }

    /// Check if the tensor is safe to mutate.
    pub fn can_mut(&self) -> bool {
        self.handle.can_mut()
    }

    /// Assert that both tensors are on the same device.
    pub fn assert_is_on_same_device(&self, other: &Self) {
        if self.device != other.device {
            panic!(
                "Both tensors should be on the same device {:?} != {:?}",
                self.device, other.device
            );
        }
    }

    /// Check if the current tensor is contiguous.
    ///
    /// A tensor is contiguous if the elements are stored in memory
    /// if the strides in non-increasing order and the
    /// strides at position k is equal to the product of the shapes
    /// at all positions greater than k. However, all axes with a shape of 1 are ignored.
    pub fn is_contiguous(&self) -> bool {
        is_contiguous(self.meta.shape(), self.meta.strides())
    }

    /// Check if the current tensor has a contiguous backing buffer (no overlap and no empty memory
    /// regions within the shape).

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Call `.to_device(&device)` on one of the tensors to bring both onto the same device before the operation.
  2. Standardize on a single `device` variable (e.g. from `B::Device::default()`) and use it for every tensor creation, model `.to_device()`, and data load.
  3. If using multi-GPU, explicitly place both operands on the device of the current process/rank (see burn distributed / DDP patterns).
  4. Check that model parameters were moved with `model.to_device(&device)` after loading, not just the input data.

Example fix

// before
let model = Model::new(&device_a).to_device(&device_a);
let input = Tensor::from_data(data, &device_b);
let out = model.forward(input); // panics: device_a != device_b

// after
let input = Tensor::from_data(data, &device_a); // same device as model
let out = model.forward(input);
Defensive patterns

Strategy: validation

Validate before calling

// before the op
assert_eq!(t1.device(), t2.device(), "tensors on different devices: {:?} vs {:?}", t1.device(), t2.device());

Type guard

// Rust has no runtime type guard; use an explicit check helper
fn same_device<D: burn::tensor::DeviceOps>(a: &D, b: &D) -> bool { a == b }

Try / catch

// panic-based; cannot be caught — align devices instead
let t2 = t2.to_device(&t1.device());

Prevention

When it happens

Trigger: Any binary or multi-tensor operation (e.g. element-wise ops, matmul, cat, comparison ops) built on CubeTensor where the two inputs were created on, moved to, or lazily computed on different devices: `t1 = Tensor::<Backend, 2>::from_data(data1, &device_g0)` combined with `t2` on device_g1 or on the CPU device.

Common situations: Multi-GPU setups where weights were initialized on GPU 0 but inputs were sent to GPU 1; mixing CPU-created tensors with GPU tensors; loading a checkpoint/model saved with a CPU device record and running it against a GPU device; wrapping tensors in a struct created once with an old device reference.

Related errors


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