{"record":{"id":"38c5ff15ce6533cc","repo":"tracel-ai/burn","slug":"autodiff-should-not-wrap-an-autodiff-tensor-38c5ff","errorCode":null,"errorMessage":"Autodiff should not wrap an autodiff tensor.","messagePattern":"Autodiff should not wrap an autodiff tensor\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-dispatch/src/backend.rs","lineNumber":348,"sourceCode":"\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))),\n                #[cfg(any(feature = \"flex\", default_backend))]\n                DispatchTensorKind::Flex(tensor) => tensor\n                    .as_autodiff()","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-dispatch/src/backend.rs#L330-L366","documentation":"DispatchTensorKind::Autodiff is a wrapper that must contain exactly one concrete backend tensor (NdArray, LibTorch, etc.). If backward() encounters an Autodiff kind wrapped inside another Autodiff kind, the internal invariant 'no double autodiff wrapping' is violated and the code panics. Hitting it indicates a tensor-construction bug in dispatch code, not a user-recoverable condition.","triggerScenarios":"Dispatch::backward() on a tensor whose outer kind is Autodiff and whose inner kind is also Autodiff — i.e. Autodiff(Autodiff(...)) — typically from calling backward() on a tensor that already went through inner()/autodiff unwrapping incorrectly.","commonSituations":"Custom backend glue or plugin code that wraps an already-autodiffed DispatchTensor again; mixing tensor values from different dispatch layers; library version mismatch where wrapping logic changed.","solutions":["Unwrap to the inner backend tensor before calling backward() (use AutodiffBackend::inner)","Audit code that constructs DispatchTensorKind::Autodiff so it never wraps an already-wrapped tensor","Update burn-dispatch and dependent crates to matching versions"],"exampleFix":"// before\nlet grads = Dispatch::backward(double_wrapped_tensor);\n// after\nlet inner_tensor = Dispatch::inner(double_wrapped_tensor);\nlet grads = Dispatch::backward(inner_tensor);","handlingStrategy":"type-guard","validationCode":"fn ensure_single_autodiff_wrap(t: &DispatchTensor) -> Result<(), String> {\n    if let DispatchTensorKind::Autodiff(inner) = &t.kind {\n        if matches!(**inner, DispatchTensorKind::Autodiff(_)) {\n            return Err(\"tensor is double-wrapped in Autodiff\".into());\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_double_wrapped(t: &DispatchTensor) -> bool {\n    matches!(&t.kind, DispatchTensorKind::Autodiff(inner)\n        if matches!(**inner, DispatchTensorKind::Autodiff(_)))\n}","tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(|| Dispatch::backward(t)));\nif result.is_err() { eprintln!(\"double autodiff wrap detected\"); }","preventionTips":["Never wrap an already-wrapped DispatchTensor in Autodiff again","Use inner()/AutodiffBackend::inner() to unwrap before re-wrapping","Pin all burn crates to the same version"],"tags":["rust","autodiff","invariant-violation","panic"],"backgroundTag":"double-autodiff-wrap","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"}