tracel-ai/burn · error

Unexpected Autodiff variant provided to `from_backend`

Error message

Unexpected Autodiff variant provided to `from_backend`

What it means

The `from_backend` macro-generated constructor re-tags each `BackendTensor` variant into a backend-specific `DispatchTensorKind`. The `Autodiff` variant has no valid target in this conversion, so encountering one is an internal misuse and the code panics with 'Unexpected Autodiff variant provided to `from_backend`'.

Source

Thrown at crates/burn-dispatch/src/tensor.rs:568

                let kind = match tensor {
                    // Inverse: Wrap the `Float` variant back into the backend's `Autodiff` primitive variant
                    BackendTensor::Float(t) => {
                        let ad_tensor = BackendTensor::Autodiff(t);
                        // Wrap in the concrete backend's dispatch container
                        let inner_dispatch = DispatchTensorKind::$backend(ad_tensor);
                        // Re-apply the outer Autodiff dispatch wrapper
                        DispatchTensorKind::Autodiff(Box::new(inner_dispatch))
                    }

                    // Pass-throughs for non-differentiable types
                    BackendTensor::Int(t) => DispatchTensorKind::$backend(BackendTensor::Int(t)),
                    BackendTensor::Bool(t) => DispatchTensorKind::$backend(BackendTensor::Bool(t)),
                    BackendTensor::Quantized(t) => {
                        DispatchTensorKind::$backend(BackendTensor::Quantized(t))
                    }

                    BackendTensor::Autodiff(_) => {
                        panic!("Unexpected Autodiff variant provided to `from_backend`",)
                    }
                };

                DispatchTensor {
                    kind,
                    autodiff: DispatchAutodiffContext::Enabled(C::STRATEGY),
                }
            }
        }
    };
}

// One invocation per dispatch variant. Every cubecl runtime is the same `Cube`
// backend, so they share the one impl rather than getting seven identical ones.
impl_dispatch_conversion!(Cube, cube_backend);
impl_dispatch_conversion!(Flex, any(feature = "flex", default_backend));
impl_dispatch_conversion!(Remote, feature = "remote");
impl_dispatch_conversion!(Capture, feature = "capture");

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Strip the autodiff wrapper (extract the inner backend tensor) before calling `from_backend`.
  2. Use the autodiff-specific constructor/path that accepts `BackendTensor::Autodiff`.
  3. Add an upstream variant check and route autodiff tensors to the correct conversion.

Example fix

// before
let dt = DispatchTensor::from_backend(backend_tensor); // Autodiff variant panics
// after
let dt = match backend_tensor {
    BackendTensor::Autodiff(t) => DispatchTensor::from_backend(BackendTensor::Float(t.inner())),
    other => DispatchTensor::from_backend(other),
};
Defensive patterns

Strategy: validation

Validate before calling

if matches!(backend_tensor, BackendTensor::Autodiff(_)) { /* strip wrapper or use autodiff path */ }

Type guard

fn is_plain_backend_tensor<B: Backend>(t: &BackendTensor<B>) -> bool {
    !matches!(t, BackendTensor::Autodiff(_))
}

Try / catch

let result = std::panic::catch_unwind(AssertUnwindSafe(|| DispatchTensor::from_backend(bt.clone())));
match result {
    Ok(dt) => use_tensor(dt),
    Err(_) => eprintln!("autodiff variant passed to non-autodiff constructor"),
}

Prevention

When it happens

Trigger: Passing a `BackendTensor::Autodiff(_)` into the `from_backend` constructor for a concrete (non-autodiff) backend context — i.e. feeding an autodiff-wrapped tensor into a path that expects plain backend primitives.

Common situations: Custom backend or dispatch glue that forwards tensors without stripping the autodiff wrapper, or calling a non-autodiff constructor from autodiff-enabled code.

Related errors


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