Lightning-AI/pytorch-lightning · error · MisconfigurationException
When `optimizer.step(closure)` is called, the closure should
Error message
When `optimizer.step(closure)` is called, the closure should be callable
What it means
LightningOptimizer.step(closure) requires the closure to be callable (it is re-executed by the strategy during optimization). Passing a non-callable (e.g. a tensor, result of calling the closure, or None-like object) raises MisconfigurationException.
Source
Thrown at src/lightning/pytorch/core/optimizer.py:151
with opt_gen.toggle_model(sync_grad=accumulated_grad_batches):
opt_gen.step(closure=closure_gen)
def closure_dis():
loss_dis = self.compute_discriminator_loss(...)
self.manual_backward(loss_dis)
if accumulated_grad_batches:
opt_dis.zero_grad()
with opt_dis.toggle_model(sync_grad=accumulated_grad_batches):
opt_dis.step(closure=closure_dis)
"""
self._on_before_step()
if closure is None:
closure = do_nothing_closure
elif not callable(closure):
raise MisconfigurationException("When `optimizer.step(closure)` is called, the closure should be callable")
assert self._strategy is not None
step_output = self._strategy.optimizer_step(self._optimizer, closure, **kwargs)
self._on_after_step()
return step_output
@classmethod
def _to_lightning_optimizer(
cls, optimizer: Union[Optimizer, "LightningOptimizer"], strategy: "pl.strategies.Strategy"
) -> "LightningOptimizer":
# the user could return a `LightningOptimizer` from `configure_optimizers`, see test:
# tests/core/test_lightning_optimizer.py::test_lightning_optimizer[False]
lightning_optimizer = optimizer if isinstance(optimizer, LightningOptimizer) else cls(optimizer)
lightning_optimizer._strategy = proxy(strategy)
return lightning_optimizer
View on GitHub (pinned to 9fed5c27d2)
Solutions
- Pass the closure itself, not its result: optimizer.step(closure) where closure is a zero-arg callable
- Wrap logic in a def or lambda: optimizer.step(lambda: self.training_step(batch, batch_idx))
Example fix
# before optimizer.step(self.training_step(batch, batch_idx)) # after optimizer.step(lambda: self.training_step(batch, batch_idx))
Defensive patterns
Strategy: type-guard
Validate before calling
assert callable(closure), "closure must be callable" optimizer.step(closure)
Type guard
def is_closure(c) -> bool:
return callable(c) Prevention
- Pass method references or lambdas, never call results
- Lint for optimizer.step(fn(...)) patterns
When it happens
Trigger: Calling optimizer.step(training_step(...)) — i.e. passing the closure's return value instead of the function — or passing a non-function object in manual optimization.
Common situations: Manual optimization code that accidentally invokes the closure: optimizer.step(self.training_step(batch, batch_idx)) instead of passing the method reference.
Related errors
- Training with multiple optimizers is only supported with man
- Training with multiple optimizers is only supported with man
- An optimizer should be passed only once to the `setup` metho
- Automatic gradient accumulation and the `GradientAccumulatio
- A single `Optimizer` cannot have multiple parameter groups w
AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28).
Data as JSON: /api/errors/749a215bdb3a9cc1.
Report an issue: GitHub.