{"record":{"id":"1615617c41f44cf2","repo":"Lightning-AI/pytorch-lightning","slug":"training-with-multiple-optimizers-is-only-supporte-161561","errorCode":null,"errorMessage":"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()`.","messagePattern":"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\\(\\)`\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"src/lightning/pytorch/core/optimizer.py","lineNumber":359,"sourceCode":"            and not is_overridden(\"lr_scheduler_step\", model)\n            and model.automatic_optimization\n        ):\n            raise MisconfigurationException(\n                f\"The provided lr scheduler `{scheduler.__class__.__name__}` doesn't follow PyTorch's LRScheduler\"\n                \" API. You should override the `LightningModule.lr_scheduler_step` hook with your own logic if\"\n                \" you are using a custom LR scheduler.\"\n            )\n\n\ndef _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:","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/core/optimizer.py#L341-L377","documentation":"Returning more than one optimizer from configure_optimizers while self.automatic_optimization is True is unsupported in Lightning 2.x; Lightning raises RuntimeError telling you to enable manual optimization and step each optimizer yourself.","triggerScenarios":"configure_optimizers returns [opt1, opt2] and the module leaves automatic_optimization at its default True.","commonSituations":"GANs, multi-head models, or meta-learning setups migrated from Lightning 1.x that relied on automatic multi-optimizer stepping with optimizer_idx.","solutions":["Set self.automatic_optimization = False in the LightningModule __init__","In training_step, get optimizers with opt1, opt2 = self.optimizers() and run zero_grad/backward/step for each","If you only need one optimizer, return a single optimizer from configure_optimizers"],"exampleFix":"# before\n# automatic_optimization default True, configure_optimizers returns [opt1, opt2]\n# after\ndef __init__(self):\n    super().__init__()\n    self.automatic_optimization = False\n\ndef training_step(self, batch, batch_idx):\n    opt_gen, opt_disc = self.optimizers()\n    ...  # manual backward/step per optimizer","handlingStrategy":"validation","validationCode":"n = len(model.configure_optimizers()[0]) if isinstance(model.configure_optimizers(), (list, tuple)) else 1\nif n > 1:\n    assert model.automatic_optimization is False, \"multi-optimizer requires manual optimization\"","typeGuard":"def multi_opt_ok(module) -> bool:\n    return not (len(getattr(module, \"_optimizers\", [])) > 1 and module.automatic_optimization)","tryCatchPattern":null,"preventionTips":["Set automatic_optimization=False up front for GAN-style models","Return a single optimizer unless multi-step logic is truly needed"],"tags":["optimizer","manual-optimization","gan","lightning"],"backgroundTag":"unsupported-configuration","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}