{"record":{"id":"a7d9297e340ae8ed","repo":"tracel-ai/burn","slug":"an-autodiff-float-primitive-must-have-an-enabled-a-a7d929","errorCode":null,"errorMessage":"an autodiff float primitive must have an enabled autodiff context","messagePattern":"an autodiff float primitive must have an enabled autodiff context","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-dispatch/src/macros.rs","lineNumber":270,"sourceCode":"            |$inner, $device_ident| $body\n        )\n    };\n}\n\n/// Match arm generator for `float_to_device`.\n///\n/// Similar to `to_device_arms`, but float tensors are checked for autodiff support.\nmacro_rules! float_to_device_arms {\n    (\n        $tensor:expr, $device:expr, $to_device:ident, |$inner:ident, $device_ident:ident| $body:expr;\n        $( [$B1:ident, $src_cfg:meta] => [ $( [$B2:ident, $dst_cfg:meta] ),+ ] );*\n    ) => {\n        #[allow(unreachable_patterns)]\n        match ($tensor.kind, $device) {\n            #[cfg(feature = \"autodiff\")]\n            ($crate::DispatchTensorKind::Autodiff(kind), $crate::DispatchDevice::Autodiff(device)) => {\n                let $crate::DispatchAutodiffContext::Enabled(ckp) = $tensor.autodiff else {\n                    panic!(\"an autodiff float primitive must have an enabled autodiff context\")\n                };\n                float_to_device_arms!(\n                    @autodiff\n                    *kind, &**device, ckp, $to_device;\n                    $([$B1, $src_cfg]);*\n                )\n\n            }\n            // Capture is deliberately absent from the cross-backend matrix. Same-backend movement\n            // remains available; CaptureBackend decides whether the particular device transfer is\n            // valid (computed tensors can only remain in their capture session).\n            #[cfg(feature = \"capture\")]\n            ($crate::DispatchTensorKind::Capture(kind), $crate::DispatchDevice::Capture(d)) => {\n                $crate::DispatchTensor {\n                    kind: $crate::DispatchTensorKind::Capture($crate::BackendTensor::Float(\n                        $crate::backends::Capture::$to_device(kind.float(), d)\n                    )),\n                    autodiff: $tensor.autodiff,","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-dispatch/src/macros.rs#L252-L288","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet t = tensor_factory(); // autodiff context Disabled\nlet ctx = autodiff.enable();\nt.to_device(&dev); // panics: context not enabled for this tensor\n\n// after\nlet ctx = autodiff.enable();\nlet t = tensor_factory(); // created under Enabled context\nlet moved = t.to_device(&dev);","handlingStrategy":"validation","validationCode":"// before the move\nif matches!(tensor.kind, DispatchTensorKind::Autodiff(_))\n    && !tensor.autodiff_enabled() {\n    panic!(\"enter an enabled autodiff context (or detach) before moving autodiff tensors\");\n}","typeGuard":"fn has_enabled_context(t: &DispatchPrimitive) -> bool {\n    matches!(t.autodiff, DispatchAutodiffContext::Enabled(_))\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","autodiff","tensor","checkpointing","burn"],"backgroundTag":"autodiff-context-not-enabled","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}