tracel-ai/burn · error
an autodiff float primitive must have an enabled autodiff co
Error message
an autodiff float primitive must have an enabled autodiff context
What it means
An autodiff float tensor (DispatchTensorKind::Autodiff) was matched with an Autodiff device, but its `autodiff` field held DispatchAutodiffContext::Disabled (or otherwise not Enabled) when the macro required a live checkpointing context. To move or operate on an autodiff float primitive the dispatcher needs the enabled autodiff context (carrying the checkpointer) to route gradients; its absence is an invariant violation, so the macro panics.
Source
Thrown at crates/burn-dispatch/src/macros.rs:270
|$inner, $device_ident| $body
)
};
}
/// Match arm generator for `float_to_device`.
///
/// Similar to `to_device_arms`, but float tensors are checked for autodiff support.
macro_rules! float_to_device_arms {
(
$tensor:expr, $device:expr, $to_device:ident, |$inner:ident, $device_ident:ident| $body:expr;
$( [$B1:ident, $src_cfg:meta] => [ $( [$B2:ident, $dst_cfg:meta] ),+ ] );*
) => {
#[allow(unreachable_patterns)]
match ($tensor.kind, $device) {
#[cfg(feature = "autodiff")]
($crate::DispatchTensorKind::Autodiff(kind), $crate::DispatchDevice::Autodiff(device)) => {
let $crate::DispatchAutodiffContext::Enabled(ckp) = $tensor.autodiff else {
panic!("an autodiff float primitive must have an enabled autodiff context")
};
float_to_device_arms!(
@autodiff
*kind, &**device, ckp, $to_device;
$([$B1, $src_cfg]);*
)
}
// Capture is deliberately absent from the cross-backend matrix. Same-backend movement
// remains available; CaptureBackend decides whether the particular device transfer is
// valid (computed tensors can only remain in their capture session).
#[cfg(feature = "capture")]
($crate::DispatchTensorKind::Capture(kind), $crate::DispatchDevice::Capture(d)) => {
$crate::DispatchTensor {
kind: $crate::DispatchTensorKind::Capture($crate::BackendTensor::Float(
$crate::backends::Capture::$to_device(kind.float(), d)
)),
autodiff: $tensor.autodiff,View on GitHub (pinned to d16f7ba2ed)
Solutions
- Create and use the tensor inside an enabled autodiff context (Autodiff::backend `context` / grad-enabled scope) so the autodiff field is DispatchAutodiffContext::Enabled(checkpointer).
- Detach the tensor to a plain backend tensor (`.inner()`) if gradient tracking is not needed — then no autodiff context is required.
- Reorder code so the to-device move happens while the autodiff context is still alive, not after it is dropped.
- In custom dispatch code, match on the context and handle Disabled explicitly rather than relying on the panic path.
Example fix
// before let t = tensor_factory(); // autodiff context Disabled let ctx = autodiff.enable(); t.to_device(&dev); // panics: context not enabled for this tensor // after let ctx = autodiff.enable(); let t = tensor_factory(); // created under Enabled context let moved = t.to_device(&dev);
Defensive patterns
Strategy: validation
Validate before calling
// before the move
if matches!(tensor.kind, DispatchTensorKind::Autodiff(_))
&& !tensor.autodiff_enabled() {
panic!("enter an enabled autodiff context (or detach) before moving autodiff tensors");
} Type guard
fn has_enabled_context(t: &DispatchPrimitive) -> bool {
matches!(t.autodiff, DispatchAutodiffContext::Enabled(_))
} Prevention
- Create autodiff tensors only inside an enabled grad context.
- Drop or detach autodiff tensors before the context exits.
- In tests, construct primitives through library helpers rather than by hand.
When it happens
Trigger: Calling the autodiff to-device / operation macro (macros.rs:270) with a tensor whose autodiff context was never initialized or was already closed (Disabled), while both tensor.kind and target device are autodiff — e.g. a tensor created outside an AutodiffEnabled context then used inside one, or the context dropped before the transfer.
Common situations: Constructing tensors before entering `context`/grad-enabled scope and moving them after the scope exits; manually assembling Dispatch primitives in tests without setting the Enabled context; checkpoint builder lifecycle bugs where the checkpointer is consumed too early.
Related errors
- Operation not marked for autodiff.
- Cannot move between autodiff and non-autodiff instances.
- Requires autodiff tensor.
- Cannot move a tensor from a capture device
- Autodiff float tensor is on the wrong backend (expected {bac
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/a7d9297e340ae8ed.
Report an issue: GitHub.