tracel-ai/burn · error

Autodiff should not wrap an autodiff tensor.

Error message

Autodiff should not wrap an autodiff tensor.

What it means

DispatchTensorKind::Autodiff is a wrapper that must contain exactly one concrete backend tensor (NdArray, LibTorch, etc.). If backward() encounters an Autodiff kind wrapped inside another Autodiff kind, the internal invariant 'no double autodiff wrapping' is violated and the code panics. Hitting it indicates a tensor-construction bug in dispatch code, not a user-recoverable condition.

Source

Thrown at crates/burn-dispatch/src/backend.rs:348

        match kind {
            DispatchTensorKind::Autodiff(tensor) => match *tensor {
                #[cfg(cube_backend)]
                DispatchTensorKind::Cube(tensor) => tensor.autodiff().backward(),
                #[cfg(any(feature = "flex", default_backend))]
                DispatchTensorKind::Flex(tensor) => tensor.autodiff().backward(),
                #[cfg(feature = "ndarray")]
                DispatchTensorKind::NdArray(tensor) => tensor.autodiff().backward(),
                #[cfg(feature = "tch")]
                DispatchTensorKind::LibTorch(tensor) => tensor.autodiff().backward(),
                #[cfg(feature = "remote")]
                DispatchTensorKind::Remote(tensor) => tensor.autodiff().backward(),
                #[cfg(feature = "capture")]
                DispatchTensorKind::Capture(_) => {
                    panic!("Capture tensors do not support autodiff")
                }
                DispatchTensorKind::Autodiff(_) => {
                    panic!("Autodiff should not wrap an autodiff tensor.")
                }
            },
            _ => panic!("Requires autodiff tensor."),
        }
    }

    fn grad(tensor: &DispatchTensor, grads: &Self::Gradients) -> Option<DispatchTensor> {
        let DispatchTensor { kind, .. } = tensor;
        let grad: Option<DispatchTensorKind> = match &kind {
            DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
                #[cfg(cube_backend)]
                DispatchTensorKind::Cube(tensor) => tensor
                    .as_autodiff()
                    .grad(grads)
                    .map(|t| DispatchTensorKind::Cube(crate::BackendTensor::Float(t))),
                #[cfg(any(feature = "flex", default_backend))]
                DispatchTensorKind::Flex(tensor) => tensor
                    .as_autodiff()

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Unwrap to the inner backend tensor before calling backward() (use AutodiffBackend::inner)
  2. Audit code that constructs DispatchTensorKind::Autodiff so it never wraps an already-wrapped tensor
  3. Update burn-dispatch and dependent crates to matching versions

Example fix

// before
let grads = Dispatch::backward(double_wrapped_tensor);
// after
let inner_tensor = Dispatch::inner(double_wrapped_tensor);
let grads = Dispatch::backward(inner_tensor);
Defensive patterns

Strategy: type-guard

Validate before calling

fn ensure_single_autodiff_wrap(t: &DispatchTensor) -> Result<(), String> {
    if let DispatchTensorKind::Autodiff(inner) = &t.kind {
        if matches!(**inner, DispatchTensorKind::Autodiff(_)) {
            return Err("tensor is double-wrapped in Autodiff".into());
        }
    }
    Ok(())
}

Type guard

fn is_double_wrapped(t: &DispatchTensor) -> bool {
    matches!(&t.kind, DispatchTensorKind::Autodiff(inner)
        if matches!(**inner, DispatchTensorKind::Autodiff(_)))
}

Try / catch

let result = std::panic::catch_unwind(AssertUnwindSafe(|| Dispatch::backward(t)));
if result.is_err() { eprintln!("double autodiff wrap detected"); }

Prevention

When it happens

Trigger: Dispatch::backward() on a tensor whose outer kind is Autodiff and whose inner kind is also Autodiff — i.e. Autodiff(Autodiff(...)) — typically from calling backward() on a tensor that already went through inner()/autodiff unwrapping incorrectly.

Common situations: Custom backend glue or plugin code that wraps an already-autodiffed DispatchTensor again; mixing tensor values from different dispatch layers; library version mismatch where wrapping logic changed.

Related errors


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