tracel-ai/burn · error
Autodiff should not wrap an autodiff tensor.
Error message
Autodiff should not wrap an autodiff tensor.
What it means
This panic is emitted by code generated by the burn-backend-extension dispatch macro. The AutodiffKindMatch route is generated such that if the runtime dispatch tensor already carries an Autodiff kind, wrapping it again in autodiff is invalid (autodiff must wrap a plain backend, never another autodiff backend), so the generated match arm panics.
Source
Thrown at crates/burn-backend-extension/src/dispatch.rs:197
source_kind: TokenStream,
inner_kind: TokenStream,
) -> TokenStream {
let float_inputs = float_input_presence(operation, source);
let backends = dispatch_backends();
let source_is_non_float = source.is_some_and(|source| {
matches!(
source.kind,
InputKind::Tensor { kind, .. } if kind != TensorKind::Float
)
});
let paths = routing::RoutingPaths::dispatch();
let selected =
source.map(|_| syn::Ident::new("__burn_selected", proc_macro2::Span::call_site()));
let autodiff_match = (!source_is_non_float).then(|| routing::AutodiffKindMatch {
cfg_attr: Some(quote!(#[cfg(feature = "autodiff")])),
inner_kind,
nested_autodiff: Some(quote! {
panic!("Autodiff should not wrap an autodiff tensor.")
}),
fallback: quote!(panic!("unsupported dispatch backend")),
});
let arms = routing::backend_kind_arms(
&paths,
&backends,
selected.as_ref(),
autodiff_match,
|_, backend, autodiff| {
backend_route_body(backend, operation, &float_inputs, autodiff, source)
},
);
let direct_arms = arms.concrete;
let autodiff_route = if source_is_non_float {
quote! {
#[cfg(feature = "autodiff")]
crate::DispatchTensorKind::Autodiff(_) => {
panic!("only float tensors may use an autodiff primitive")View on GitHub (pinned to d16f7ba2ed)
Solutions
- Remove the double wrap: use Autodiff<B> once at the outermost boundary only
- Pass the inner (non-autodiff) tensor into the dispatch call, unwrapping any AutodiffTensor first
- Disable the autodiff feature for the dispatch macro invocation if the call site is not a training boundary
- Fix generic bounds so autodiff wrapping happens only where the kind is not already Autodiff
Example fix
// before let x = AutodiffTensor::from(AutodiffTensor::from(inner_tensor)); // nested // after let x = AutodiffTensor::from(inner_tensor);
Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure you never double-wrap: if T is already Autodiff<...>, do not wrap again
fn assert_single_wrap<B: Backend>(_t: &AutodiffTensor<B>) {} // compile-time: B must be a plain backend Prevention
- Wrap the innermost concrete backend only once: Autodiff<Wgpu>, never Autodiff<Autodiff<Wgpu>>
- Unwrap AutodiffTensor to its inner tensor before dispatch calls
- Keep autodiff wrapping at a single boundary in the codebase
- Review macro invocations so nested_autodiff arms are not hit
When it happens
Trigger: Passing a tensor already typed as Autodiff<B, C> through the dispatch machinery that tries to re-wrap it as autodiff (nested autodiff); misconfigured backend generics that resolve the autodiff arm twice.
Common situations: Double-wrapping backends like Autodiff<Autodiff<Wgpu>>; macro-generated dispatch functions receiving autodiff tensors from an outer layer; backend-agnostic code compiled with the autodiff feature fed already-tracked tensors.
Related errors
- only float tensors may use an autodiff primitive
- Can't differentiate avg pool 2d backward.
- Can't differentiate max pool2d with indices backward.
- Can't differentiate adaptive avg pool2d backward.
- Can't differentiate adaptive avg pool3d backward.
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/3e797bbd72e929d8.
Report an issue: GitHub.