tracel-ai/burn · error

an enabled float tensor must use an autodiff primitive

Error message

an enabled float tensor must use an autodiff primitive

What it means

Generated by extract_selected_input for float tensor inputs routed to a non-autodiff primitive: the macro requires DispatchAutodiffContext::Disabled. If the context is Enabled (the tensor carries autodiff state), the operation panics because an enabled float tensor must go through the autodiff primitive, not the concrete one.

Source

Thrown at crates/burn-backend-extension/src/routing.rs:541

    let name = &input.name;
    let selected = format_ident!("__burn_selected");
    let dispatch_root = &extraction.paths.dispatch_root;
    let backend_alias = &extraction.paths.backend_alias;
    let context = quote!(#dispatch_root::DispatchAutodiffContext);
    assert!(
        kind == TensorKind::Float || !autodiff_variant,
        "only a float input can directly select an autodiff primitive"
    );
    let validate_context = if kind == TensorKind::Float && autodiff_variant {
        quote! {
            let #context::Enabled(_) = __burn_selected_context else {
                panic!("an autodiff float primitive must have an enabled autodiff context")
            };
        }
    } else if kind == TensorKind::Float {
        quote! {
            let #context::Disabled = __burn_selected_context else {
                panic!("an enabled float tensor must use an autodiff primitive")
            };
        }
    } else {
        TokenStream::new()
    };

    if kind == TensorKind::Float && extraction.autodiff && !autodiff_variant {
        let autodiff_trait = &extraction.paths.autodiff_trait;
        if borrowed {
            let lifted = format_ident!("__lifted_{name}");
            quote! {
                #validate_context
                let #lifted = <#backend_alias as #autodiff_trait>::from_inner(#selected.as_float().clone());
                let #name = &#lifted;
            }
        } else {
            quote! {
                #validate_context

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Detach/stop tracking gradients on the tensor (e.g. .detach()) before passing it to the concrete dispatch path
  2. Or route through the autodiff-enabled operation variant instead
  3. Keep Enabled/Disabled tensor contexts consistent per operation call

Example fix

// before
let out = op(tensor.require_grad()); // Enabled context into concrete path
// after
let out = op(tensor.detach()); // Disabled context for concrete dispatch
Defensive patterns

Strategy: type-guard

Validate before calling

fn require_disabled_context(t: &DispatchTensor) -> Result<(), String> {
    match t.autodiff {
        DispatchAutodiffContext::Disabled => Ok(()),
        _ => Err("enabled float tensor must use the autodiff primitive; detach first".into()),
    }
}

Type guard

fn is_grad_disabled(t: &DispatchTensor) -> bool {
    matches!(t.autodiff, DispatchAutodiffContext::Disabled)
}

Try / catch

let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| op(&tensor)))
    .map_err(|_| "tensor has gradient tracking; call .detach() or use the autodiff variant".to_string())?

Prevention

When it happens

Trigger: Calling an operation whose selected float input resolves to the concrete backend primitive while the tensor's autodiff context is Enabled — e.g. passing a require_grad()/autodiff-wrapped tensor into a path compiled for the plain backend.

Common situations: Passing gradient-tracking tensors into inference-only ops; mixing autodiff-enabled and disabled tensors in one dispatch call; toggling require_grad on a tensor then routing it through a concrete-backend extension.

Related errors


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