tracel-ai/burn · error
Cannot move between autodiff and non-autodiff instances.
Error message
Cannot move between autodiff and non-autodiff instances.
What it means
A cross-device tensor move was attempted where exactly one side is autodiff: either the tensor is an Autodiff-kind tensor while the target device is a plain backend, or the tensor is plain while the target is an Autodiff device. The dispatcher requires both sides to agree on autodiffness — gradients/checkpointer state cannot be created or dropped implicitly mid-transfer, so the mismatch panics.
Source
Thrown at crates/burn-dispatch/src/macros.rs:338
// --- Cross backend arms ---
// This loop generates the grid of combinations
$(
$(
#[cfg(all($src_cfg, $dst_cfg))]
($crate::DispatchTensorKind::$B1(kind), $crate::DispatchDevice::$B2($device_ident)) => {
type B1 = $crate::backends::$B1;
type B2 = $crate::backends::$B2;
let $inner = kind.float();
$crate::DispatchTensor {
kind: $crate::DispatchTensorKind::$B2($crate::BackendTensor::Float($body)),
autodiff: $tensor.autodiff,
}
}
)+
)*
#[cfg(feature = "autodiff")]
($crate::DispatchTensorKind::Autodiff(..), _) | (_, $crate::DispatchDevice::Autodiff(_)) => panic!("Cannot move between autodiff and non-autodiff instances."),
// Capture is intentionally one-way: initialized values can be moved onto a
// capture device, but captured tensors have no materialized data to move back.
#[cfg(feature = "capture")]
($crate::DispatchTensorKind::Capture(_), _) => {
panic!("Cannot move a tensor from a capture device")
}
}
};
// Autodiff(DispatchTensor)
(
@autodiff
$tensor:expr, $device:expr, $ckp:expr, $to_device:ident;
$( [$B1:ident, $src_cfg:meta] );*
) => {{
match ($tensor, $device) {
// --- Same backend to_device ---
$(View on GitHub (pinned to d16f7ba2ed)
Solutions
- Match autodiffness on both sides: to move off autodiff, detach first (`.inner()`) and move the plain tensor to the plain device.
- To move a plain tensor into autodiff tracking, wrap the target as DispatchDevice::Autodiff and recreate/track the tensor under the enabled autodiff context.
- Use a single device type throughout a pipeline (either always Autodiff<Backend> during training or always Backend during inference).
- Check device construction helpers so the same feature flags/config produce consistent device wrappers.
Example fix
// before autodiff_tensor.to_device(&plain_backend_device); // mismatch -> panic // after let plain = autodiff_tensor.inner(); // detach gradients plain.to_device(&plain_backend_device);
Defensive patterns
Strategy: type-guard
Validate before calling
fn sides_agree(t: &DispatchPrimitive, d: &DispatchDevice) -> bool {
let t_auto = matches!(t.kind, DispatchTensorKind::Autodiff(_));
let d_auto = matches!(d, DispatchDevice::Autodiff(_));
t_auto == d_auto
} Type guard
fn compatible_move(t: &DispatchPrimitive, d: &DispatchDevice) -> bool {
matches!(t.kind, DispatchTensorKind::Autodiff(_)) == matches!(d, DispatchDevice::Autodiff(_))
} Prevention
- Detach autodiff tensors before moving to plain backend devices.
- Use one device wrapper type per pipeline stage (train: Autodiff<Backend>, infer: Backend).
- Add a debug_assert comparing autodiffness of tensor and device at move sites.
When it happens
Trigger: Calling to_device (macros.rs:338 arm) with (Autodiff tensor, plain device) or (plain tensor, DispatchDevice::Autodiff(_)) — e.g. moving a tensor tracked by autodiff onto a raw backend device, or pushing an inference tensor onto an autodiff-wrapped device.
Common situations: Mixing inference-mode and training-mode device handles in the same trainer; building model/server code where one path uses Autodiff<Backend> and another uses Backend and tensors cross between them; hot-swapping devices after toggling the autodiff feature.
Related errors
- Operation not marked for autodiff.
- Cannot move a tensor from a capture device
- an autodiff float primitive must have an enabled autodiff co
- Requires autodiff tensor.
- Autodiff float tensor is on the wrong backend (expected {bac
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/8dff2e47c80181e7.
Report an issue: GitHub.