{"record":{"id":"29c1cde5716f5b24","repo":"tracel-ai/burn","slug":"should-match-at-least-one-parameter-group-29c1cd","errorCode":null,"errorMessage":"Should match at least one parameter group.","messagePattern":"Should match at least one parameter group\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-optim/src/optim/module/module_optimizer.rs","lineNumber":195,"sourceCode":"        grads: MultiGradientsParams,\n    ) -> M {\n        self.step_common(lr_module.into(), module, grads.into())\n    }\n\n    fn optim_from_param(\n        &self,\n        id: ParamId,\n        path: Option<&str>,\n    ) -> (&'_ Arc<dyn DynOptimizer>, Option<GradientClipping>) {\n        self.optimizers\n            .iter()\n            .filter_map(|val| {\n                val.group\n                    .matches(&id, path)\n                    .then_some((&val.optim, val.grad_clipping.clone()))\n            })\n            .next_back()\n            .expect(\"Should match at least one parameter group.\")\n    }\n\n    /// Decompose the optimizer state into a serializable [`OptimizerRecord`].\n    pub fn to_record(&self) -> OptimizerRecord {\n        let mut tensors = Vec::new();\n        let mut scalars = BTreeMap::new();\n        let mut paths = BTreeMap::new();\n\n        for (id, param_state) in self.param_context.iter() {\n            let prefix = id.val().to_string();\n            let mut sink = StateSink::default();\n            param_state\n                .optim\n                .state_flatten(&prefix, &param_state.state, &mut sink);\n\n            // Persist the parameter rank explicitly so the state can be reconstructed even when it\n            // carries no tensors, and without inferring the rank from tensor shapes.\n            scalars.insert(","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-optim/src/optim/module/module_optimizer.rs#L177-L213","documentation":"During `load_record`, each saved tensor's (parameter id, path) is matched against the optimizer's parameter groups; `optim_from_param` takes the LAST matching group via `.next_back().expect(\"Should match at least one parameter group.\")`. If no group in the optimizer matches the parameter id/path from the record, the expect panics. This means the optimizer state being loaded belongs to a different model/config than the current optimizer's groups.","triggerScenarios":"Calling `load_record` with an `OptimizerRecord` whose tensor param ids/paths match none of the current optimizer's `ParamGroup`s — e.g. loading a checkpoint saved from a differently structured model, or after changing group definitions in the optimizer config.","commonSituations":"Restoring training from a checkpoint after renaming/restructuring modules; switching optimizer group configs between save and load; loading a record from another experiment's checkpoint; library version change that altered path or id derivation.","solutions":["Load the optimizer record into an optimizer built from the same model structure and config that saved it.","Use `to_record`/`load_record` as a matched pair from the same session/version.","Regenerate the checkpoint if the model structure changed, or migrate the record's ids/paths.","Add a guard comparing record param ids to `self.optimizers` group ids before calling `load_record`."],"exampleFix":"// before\nlet record: OptimizerRecord = bincode::deserialize(&checkpoint_optimizer)?;\noptimizer.load_record(record); // panics if model changed\n// after\nlet record: OptimizerRecord = bincode::deserialize(&checkpoint_optimizer)?;\nassert!(!record.tensors.is_empty(), \"checkpoint has no optimizer tensors\");\n// rebuild optimizer from the checkpoint's model, then:\noptimizer.load_record(record);","handlingStrategy":"validation","validationCode":"fn record_matches(record: &OptimizerRecord, opt: &impl ToRecord) -> bool {\n    // every tensor's param id should be known to the current optimizer groups\n    !record.tensors.is_empty() && record.tensors.iter().all(|t| t.param_id.is_some())\n}\nassert!(record_matches(&record, &optimizer), \"checkpoint optimizer does not match current model\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Save and load optimizer records with the same model structure and config","Version-tag checkpoints and validate before load","Regenerate checkpoints after model restructuring"],"tags":["rust","panic","checkpoint","optimizer","record"],"backgroundTag":"model-checkpoint-mismatch","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"}