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

  1. Match autodiffness on both sides: to move off autodiff, detach first (`.inner()`) and move the plain tensor to the plain device.
  2. To move a plain tensor into autodiff tracking, wrap the target as DispatchDevice::Autodiff and recreate/track the tensor under the enabled autodiff context.
  3. Use a single device type throughout a pipeline (either always Autodiff<Backend> during training or always Backend during inference).
  4. 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

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


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