tracel-ai/burn · error

Should have at least one optimizer

Error message

Should have at least one optimizer

What it means

`grad_clipping()` on a module-level optimizer expects the internal `optimizers` vector to be non-empty and calls `.first().expect("Should have at least one optimizer")`. If the `ModuleOptimizer` was constructed without any per-parameter-group optimizers registered, the unwrap panics instead of returning an Option. This is an internal invariant check: a module optimizer with zero optimizers is a construction bug, not a runtime condition.

Source

Thrown at crates/burn-optim/src/optim/module/module_optimizer.rs:92

                grad_clipping: None,
            }],
        }
    }
}

impl ModuleOptimizer {
    /// Check if the optimizer has gradient clipping.
    /// If there are multiple optimizers, checks if any group has gradient clipping.
    pub fn has_gradient_clipping(&self) -> bool {
        self.optimizers.iter().any(|g| g.grad_clipping.is_some())
    }

    /// Access the gradient clipping.
    /// If there are multiple optimizers, returns the first optimizer's [GradientClipping].
    pub fn grad_clipping(&self) -> Option<&GradientClipping> {
        self.optimizers
            .first()
            .expect("Should have at least one optimizer")
            .grad_clipping
            .as_ref()
    }

    /// Sets the gradient clipping.
    /// If there are multiple optimizers, assigns it to the first one.
    ///
    /// # Arguments
    ///
    /// * `gradient_clipping` - The gradient clipping.
    ///
    /// # Returns
    ///
    /// The optimizer.
    pub fn with_grad_clipping(mut self, gradient_clipping: GradientClipping) -> Self {
        self.optimizers
            .first_mut()
            .expect("Should have at least one optimizer")

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Ensure the `ModuleOptimizer` is built through its normal constructor/init path so every parameter group registers an optimizer before `grad_clipping()` is called.
  2. If building manually, guarantee at least one entry is pushed into `optimizers` before use.
  3. Check that the optimizer record/state you loaded actually contains parameter groups (see `to_record`/`load_record`).
  4. Replace direct construction with `OptimizerAdapter::from_optimizer` style APIs that derive groups from the model.

Example fix

// before
let opt: ModuleOptimizer<MyOpt> = ModuleOptimizer { optimizers: vec![] };
opt.grad_clipping(); // panics
// after
let opt = OptimizerAdapter::from_optimizer(my_opt, config); // registers groups
opt.grad_clipping(); // Ok(None) or Some(&clip)
Defensive patterns

Strategy: validation

Validate before calling

fn has_optimizers<M>(opt: &ModuleOptimizer<M>) -> bool { !opt.is_empty() } // or check group count via to_record()
assert!(has_optimizers(&opt), "optimizer has no parameter groups");

Prevention

When it happens

Trigger: Calling `grad_clipping()` on a `ModuleOptimizer` whose `optimizers` vec is empty — i.e. the optimizer was created via a path that never registered any parameter-group optimizers (e.g. a default/degenerate `OptimizerAdapter` built without groups, or a record-loading path that left the vec empty).

Common situations: Constructing a `ModuleOptimizer` manually with an empty optimizer list; deserializing/loading an optimizer state that produced no groups; version or config changes that removed all parameter groups before the clipping accessor is queried (e.g. from a learning-rate/clip scheduler reading grad clipping each step).

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/2733819389ac1a6c. Report an issue: GitHub.