{"record":{"id":"926a467cfb675c95","repo":"Lightning-AI/pytorch-lightning","slug":"some-schedulers-are-attached-with-an-optimizer-tha","errorCode":null,"errorMessage":"Some schedulers are attached with an optimizer that wasn't returned from `configure_optimizers`.","messagePattern":"Some schedulers are attached with an optimizer that wasn't returned from `configure_optimizers`\\.","errorType":"exception","errorClass":"MisconfigurationException","httpStatus":null,"severity":"critical","filePath":"src/lightning/pytorch/core/optimizer.py","lineNumber":369,"sourceCode":"def _validate_multiple_optimizers_support(optimizers: list[Optimizer], model: \"pl.LightningModule\") -> None:\n    if is_param_in_hook_signature(model.training_step, \"optimizer_idx\", explicit=True):\n        raise RuntimeError(\n            \"Training with multiple optimizers is only supported with manual optimization. Remove the `optimizer_idx`\"\n            \" argument from `training_step`, set `self.automatic_optimization = False` and access your optimizers\"\n            \" in `training_step` with `opt1, opt2, ... = self.optimizers()`.\"\n        )\n    if model.automatic_optimization and len(optimizers) > 1:\n        raise RuntimeError(\n            \"Training with multiple optimizers is only supported with manual optimization. Set\"\n            \" `self.automatic_optimization = False`, then access your optimizers in `training_step` with\"\n            \" `opt1, opt2, ... = self.optimizers()`.\"\n        )\n\n\ndef _validate_optimizers_attached(optimizers: list[Optimizer], lr_scheduler_configs: list[LRSchedulerConfig]) -> None:\n    for config in lr_scheduler_configs:\n        if config.scheduler.optimizer not in optimizers:\n            raise MisconfigurationException(\n                \"Some schedulers are attached with an optimizer that wasn't returned from `configure_optimizers`.\"\n            )\n\n\ndef _validate_optim_conf(optim_conf: dict[str, Any]) -> None:\n    valid_keys = {\"optimizer\", \"lr_scheduler\", \"monitor\"}\n    extra_keys = optim_conf.keys() - valid_keys\n    if extra_keys:\n        rank_zero_warn(\n            f\"Found unsupported keys in the optimizer configuration: {set(extra_keys)}\", category=RuntimeWarning\n        )\n\n\nclass _MockOptimizer(Optimizer):\n    \"\"\"The `_MockOptimizer` will be used inplace of an optimizer in the event that `None` is returned from\n    :meth:`~lightning.pytorch.core.LightningModule.configure_optimizers`.\"\"\"\n\n    def __init__(self) -> None:","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/core/optimizer.py#L351-L387","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\ndef configure_optimizers(self):\n    sched = StepLR(Adam(self.parameters()), 1)  # hidden optimizer\n    return [Adam(self.parameters())], [sched]  # different instance\n# after\ndef configure_optimizers(self):\n    opt = Adam(self.parameters())\n    return [opt], [StepLR(opt, 1)]","handlingStrategy":"validation","validationCode":"returned = set(map(id, optimizers))\nfor cfg in sched_configs:\n    assert id(cfg.scheduler.optimizer) in returned, \"scheduler bound to foreign optimizer\"","typeGuard":"def schedulers_attached(schedulers, optimizers) -> bool:\n    opt_ids = {id(o) for o in optimizers}\n    return all(id(s.optimizer) in opt_ids for s in schedulers)","tryCatchPattern":null,"preventionTips":["Build scheduler and optimizer as a pair in one function and return both","Never instantiate throwaway optimizers when creating schedulers"],"tags":["lr-scheduler","optimizer","validation","lightning"],"backgroundTag":"invalid-configuration-shape","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}