{"record":{"id":"e121098fe5a0d00b","repo":"tracel-ai/burn","slug":"burn-store-a-panic-escaped-module-map-during-mod","errorCode":null,"errorMessage":"burn-store: a panic escaped Module::map during ModuleSnapshot::apply, leaving the module moved-from","messagePattern":"burn-store: a panic escaped Module::map during ModuleSnapshot::apply, leaving the module moved-from","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/burn-store/src/traits.rs","lineNumber":39,"sourceCode":"/// The same reasoning is why `take_mut` and `replace_with` abort rather than recover. A\n/// `Default` placeholder would be the alternative, but [`Module`] carries no such bound.\nstruct AbortOnUnwind;\n\nimpl Drop for AbortOnUnwind {\n    fn drop(&mut self) {\n        // Only reached while unwinding; the success path forgets the guard.\n        #[cfg(feature = \"std\")]\n        {\n            eprintln!(\n                \"burn-store: a panic escaped Module::map during ModuleSnapshot::apply, leaving \\\n                 the module moved-from. Aborting rather than dropping it twice.\"\n            );\n            std::process::abort();\n        }\n        // `abort` needs `std`. A panic raised while another is already unwinding ends the\n        // process the same way, so no-std gets the same guarantee by a different route.\n        #[cfg(not(feature = \"std\"))]\n        panic!(\n            \"burn-store: a panic escaped Module::map during ModuleSnapshot::apply, leaving the \\\n             module moved-from\"\n        );\n    }\n}\n\n/// Extension trait for modules that provides tensor storage functionality.\n///\n/// This trait provides convenient methods to collect tensors from any Burn module and apply\n/// them back. Collection is lazy: each [`burn_pack::Tensor`] it returns reads its data back\n/// from the device only when that data is asked for.\npub trait ModuleSnapshot: Module {\n    /// Collects the module's tensors for inspection without copying data.\n    ///\n    /// Returns [`burn_pack::Tensor`]s that materialize their data lazily, each named by its\n    /// full path in the module (`tensor.name`).\n    ///\n    /// # Arguments","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-store/src/traits.rs#L21-L57","documentation":"ModuleSnapshot::apply mutates a module in place via Module::map, temporarily moving fields out. If a user-supplied closure panics during that window, the module is left moved-from (partially destroyed). The Drop guard detects this and re-panics with this message (after aborting on std) so the invariant violation is loud instead of silently corrupt. It only fires when a panic escaped your `map` closure.","triggerScenarios":"Calling `ModuleSnapshot::apply` (or snapshot-based load/remap of a module) with a function passed to `Module::map` that panics — e.g. unwrapping a failed tensor conversion, indexing out of bounds, or asserting inside the mapping closure.","commonSituations":"Applying a snapshot whose tensors don't match expected shapes/dtypes, causing an unwrap/assert inside the user closure to fail; a panicking custom initializer or post-processing hook during load/remap; double-panic scenarios during error handling.","solutions":["Fix the panic in the closure you pass to Module::map — inspect the original panic message printed before this one.","Validate snapshot compatibility (shapes, dtypes, device) before calling apply instead of asserting inside the closure.","Return Result/error values from your mapping logic rather than unwrapping inside the closure."],"exampleFix":"// before\nsnapshot.apply(&mut model, |param, _| Ok(param.tensor.unwrap())); // unwrap may panic -> module corrupted\n// after\nsnapshot.apply(&mut model, |param, _| {\n    param.tensor.ok_or_else(|| Error::MissingParam(param.path.clone()))\n})?;","handlingStrategy":"try-catch","validationCode":"// validate snapshot compatibility before apply\nsnapshot.apply(&mut model, |param, _| {\n    param.tensor.as_ref().map(|_| ()).ok_or_else(|| format!(\"missing tensor for {}\", param.path))\n})?;","typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(||\n    snapshot.apply(&mut model, mapping_closure)\n));\nif result.is_err() { /* model may be moved-from; rebuild it */ }","preventionTips":["Never unwrap/panic inside closures passed to Module::map","Validate shapes/dtypes/devices against the snapshot before applying","Treat the module as unusable after this panic — rebuild from source"],"tags":["rust","panic","module","state-management"],"backgroundTag":"panic-during-in-place-mutation","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"}