{"record":{"id":"98e3da6c389b4a75","repo":"tracel-ai/burn","slug":"capture-tensors-do-not-support-autodiff","errorCode":null,"errorMessage":"Capture tensors do not support autodiff","messagePattern":"Capture tensors do not support autodiff","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-dispatch/src/backend.rs","lineNumber":345,"sourceCode":"\n    fn backward(tensor: DispatchTensor) -> Self::Gradients {\n        let DispatchTensor { kind, .. } = tensor;\n\n        match kind {\n            DispatchTensorKind::Autodiff(tensor) => match *tensor {\n                #[cfg(cube_backend)]\n                DispatchTensorKind::Cube(tensor) => tensor.autodiff().backward(),\n                #[cfg(any(feature = \"flex\", default_backend))]\n                DispatchTensorKind::Flex(tensor) => tensor.autodiff().backward(),\n                #[cfg(feature = \"ndarray\")]\n                DispatchTensorKind::NdArray(tensor) => tensor.autodiff().backward(),\n                #[cfg(feature = \"tch\")]\n                DispatchTensorKind::LibTorch(tensor) => tensor.autodiff().backward(),\n                #[cfg(feature = \"remote\")]\n                DispatchTensorKind::Remote(tensor) => tensor.autodiff().backward(),\n                #[cfg(feature = \"capture\")]\n                DispatchTensorKind::Capture(_) => {\n                    panic!(\"Capture tensors do not support autodiff\")\n                }\n                DispatchTensorKind::Autodiff(_) => {\n                    panic!(\"Autodiff should not wrap an autodiff tensor.\")\n                }\n            },\n            _ => panic!(\"Requires autodiff tensor.\"),\n        }\n    }\n\n    fn grad(tensor: &DispatchTensor, grads: &Self::Gradients) -> Option<DispatchTensor> {\n        let DispatchTensor { kind, .. } = tensor;\n        let grad: Option<DispatchTensorKind> = match &kind {\n            DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {\n                #[cfg(cube_backend)]\n                DispatchTensorKind::Cube(tensor) => tensor\n                    .as_autodiff()\n                    .grad(grads)\n                    .map(|t| DispatchTensorKind::Cube(crate::BackendTensor::Float(t))),","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-dispatch/src/backend.rs#L327-L363","documentation":"burn-dispatch's AutodiffBackend::backward() calls .backward() on the tensor's inner backend kind. The Capture backend (operation capture for export/compilation) has no autodiff implementation, so any backward() on a Capture-kind tensor is an immediate panic. This is an intentional unsupported-operation guard, not a recoverable failure.","triggerScenarios":"Calling Dispatch::backward(tensor) (directly or via a training loop / loss.backward()) where the tensor was created under the 'capture' feature and is a DispatchTensorKind::Capture, e.g. building a model for burn-export/capture then attempting gradient computation.","commonSituations":"Using the capture backend (tensor capture for ONNX/kernel export) while accidentally running in a training/autodiff context; enabling both 'capture' and 'autodiff' dispatch features and routing a loss tensor through capture.","solutions":["Do not call backward() on capture tensors: run capture only in inference/export paths","Switch the tensor/model to a real backend (NdArray, LibTorch, Cube, Flex, Remote) before training","Guard training code with a backend check so capture-mode tensors never reach backward()"],"exampleFix":"// before\nlet grads = loss.backward(); // panics: loss is a Capture tensor\n// after\nlet grads = match loss_kind {\n    DispatchTensorKind::Capture(_) => return Err(Error::CaptureNoAutodiff),\n    _ => loss.backward(),\n};","handlingStrategy":"validation","validationCode":"fn ensure_not_capture(t: &DispatchTensor) -> Result<(), String> {\n    if matches!(t.kind, DispatchTensorKind::Capture(_)) {\n        Err(\"capture tensors do not support autodiff; use a real backend for training\".into())\n    } else { Ok(()) }\n}","typeGuard":"fn is_capture(t: &DispatchTensor) -> bool {\n    matches!(t.kind, DispatchTensorKind::Capture(_))\n}","tryCatchPattern":"// Rust panics are not catchable with try/catch; use catch_unwind if unavoidable\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| Dispatch::backward(loss)));\nmatch result {\n    Ok(grads) => use_grads(grads),\n    Err(_) => eprintln!(\"capture tensors do not support autodiff\"),\n}","preventionTips":["Never mix the capture feature into training/autodiff code paths","Assert tensor kind before any AutodiffBackend call","Keep capture/export passes separate from training passes"],"tags":["rust","autodiff","capture-backend","panic"],"backgroundTag":"autodiff-unsupported-backend","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"}