Lightning-AI/pytorch-lightning · critical · MisconfigurationException
Some schedulers are attached with an optimizer that wasn't r
Error message
Some schedulers are attached with an optimizer that wasn't returned from `configure_optimizers`.
What it means
Every LRScheduler is bound to a specific optimizer (scheduler.optimizer). During setup Lightning verifies each scheduler's optimizer is among those returned from configure_optimizers; a scheduler attached to a foreign optimizer raises MisconfigurationException.
Source
Thrown at src/lightning/pytorch/core/optimizer.py:369
def _validate_multiple_optimizers_support(optimizers: list[Optimizer], model: "pl.LightningModule") -> None:
if is_param_in_hook_signature(model.training_step, "optimizer_idx", explicit=True):
raise RuntimeError(
"Training with multiple optimizers is only supported with manual optimization. Remove the `optimizer_idx`"
" argument from `training_step`, set `self.automatic_optimization = False` and access your optimizers"
" in `training_step` with `opt1, opt2, ... = self.optimizers()`."
)
if model.automatic_optimization and len(optimizers) > 1:
raise RuntimeError(
"Training with multiple optimizers is only supported with manual optimization. Set"
" `self.automatic_optimization = False`, then access your optimizers in `training_step` with"
" `opt1, opt2, ... = self.optimizers()`."
)
def _validate_optimizers_attached(optimizers: list[Optimizer], lr_scheduler_configs: list[LRSchedulerConfig]) -> None:
for config in lr_scheduler_configs:
if config.scheduler.optimizer not in optimizers:
raise MisconfigurationException(
"Some schedulers are attached with an optimizer that wasn't returned from `configure_optimizers`."
)
def _validate_optim_conf(optim_conf: dict[str, Any]) -> None:
valid_keys = {"optimizer", "lr_scheduler", "monitor"}
extra_keys = optim_conf.keys() - valid_keys
if extra_keys:
rank_zero_warn(
f"Found unsupported keys in the optimizer configuration: {set(extra_keys)}", category=RuntimeWarning
)
class _MockOptimizer(Optimizer):
"""The `_MockOptimizer` will be used inplace of an optimizer in the event that `None` is returned from
:meth:`~lightning.pytorch.core.LightningModule.configure_optimizers`."""
def __init__(self) -> None:View on GitHub (pinned to 9fed5c27d2)
Solutions
- Create the scheduler from the exact optimizer object you return: opt = Adam(...); sched = StepLR(opt, 1); return [opt], [sched]
- Avoid re-instantiating optimizers between configure_optimizers calls; reuse self parameters and the same objects
- When exchanging schedulers/optimizers programmatically, rebuild the scheduler against the new optimizer
Example fix
# before
def configure_optimizers(self):
sched = StepLR(Adam(self.parameters()), 1) # hidden optimizer
return [Adam(self.parameters())], [sched] # different instance
# after
def configure_optimizers(self):
opt = Adam(self.parameters())
return [opt], [StepLR(opt, 1)] Defensive patterns
Strategy: validation
Validate before calling
returned = set(map(id, optimizers))
for cfg in sched_configs:
assert id(cfg.scheduler.optimizer) in returned, "scheduler bound to foreign optimizer" Type guard
def schedulers_attached(schedulers, optimizers) -> bool:
opt_ids = {id(o) for o in optimizers}
return all(id(s.optimizer) in opt_ids for s in schedulers) Prevention
- Build scheduler and optimizer as a pair in one function and return both
- Never instantiate throwaway optimizers when creating schedulers
When it happens
Trigger: Constructing schedulers over an optimizer that is not returned: sched = StepLR(torch.optim.Adam(model.parameters()), ...) while configure_optimizers returns a different Adam instance; also common when re-running setup after _exchange_scheduler swaps schedulers.
Common situations: Creating optimizer/scheduler pairs with helper functions that instantiate fresh optimizers, or swapping optimizer state in before_configure or on resume so scheduler.optimizer no longer matches the returned list.
Related errors
- The lr scheduler dict must have the key "scheduler" with its
- The "interval" key in lr scheduler dict must be "step" or "e
- An optimizer should be passed only once to the `setup` metho
- `setup_optimizers` requires at least one optimizer as input.
- {seed} is not in bounds, numpy accepts from {min_seed_value}
AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28).
Data as JSON: /api/errors/926a467cfb675c95.
Report an issue: GitHub.