{"record":{"id":"2f8d1e8ef968bcb0","repo":"Lightning-AI/pytorch-lightning","slug":"the-provided-lr-scheduler-scheduler-class-2f8d1e","errorCode":null,"errorMessage":"The provided lr scheduler `{scheduler.__class__.__name__}` doesn't follow PyTorch's LRScheduler API. You should override the `LightningModule.lr_scheduler_step` hook with your own logic if you are using a custom LR scheduler.","messagePattern":"The provided lr scheduler `(.+?)` doesn't follow PyTorch's LRScheduler API\\. You should override the `LightningModule\\.lr_scheduler_step` hook with your own logic if you are using a custom LR scheduler\\.","errorType":"exception","errorClass":"MisconfigurationException","httpStatus":null,"severity":"error","filePath":"src/lightning/pytorch/core/optimizer.py","lineNumber":344,"sourceCode":"        lr_scheduler_configs.append(config)\n    return lr_scheduler_configs\n\n\ndef _validate_scheduler_api(lr_scheduler_configs: list[LRSchedulerConfig], model: \"pl.LightningModule\") -> None:\n    for config in lr_scheduler_configs:\n        scheduler = config.scheduler\n        if not isinstance(scheduler, _Stateful):\n            raise TypeError(\n                f\"The provided lr scheduler `{scheduler.__class__.__name__}` is invalid.\"\n                \" It should have `state_dict` and `load_state_dict` methods defined.\"\n            )\n\n        if (\n            not isinstance(scheduler, LRSchedulerTypeTuple)\n            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()`.\"","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/core/optimizer.py#L326-L362","documentation":"If a scheduler is not one of PyTorch's standard LRScheduler types (LRSchedulerTypeTuple) and the LightningModule does not override the lr_scheduler_step hook while using automatic optimization, Lightning cannot know how to call scheduler.step() and raises MisconfigurationException.","triggerScenarios":"Using a custom scheduler class (not subclassing torch LRScheduler) with automatic_optimization=True and no lr_scheduler_step override.","commonSituations":"Custom cyclic/warmup schedulers or third-party schedulers (e.g. from transformers) that expose step(epoch, metric) signatures unlike PyTorch's.","solutions":["Override lr_scheduler_step in the LightningModule: def lr_scheduler_step(self, scheduler, metric): scheduler.step(metric) (or scheduler.step())","Or refactor the scheduler to subclass torch.optim.lr_scheduler.LRScheduler"],"exampleFix":"# before\nclass LM(pl.LightningModule):\n    ...  # custom scheduler, no hook\n# after\nclass LM(pl.LightningModule):\n    def lr_scheduler_step(self, scheduler, metric):\n        if metric is None:\n            scheduler.step()\n        else:\n            scheduler.step(metric)","handlingStrategy":"type-guard","validationCode":"from lightning.pytorch.utilities.types import LRSchedulerTypeTuple\nfrom lightning.pytorch.utilities.model_helpers import is_overridden\nif not isinstance(sched, LRSchedulerTypeTuple) and not is_overridden(\"lr_scheduler_step\", model):\n    raise RuntimeError(\"override lr_scheduler_step for custom scheduler\")","typeGuard":"def custom_sched_needs_hook(sched, model) -> bool:\n    from lightning.pytorch.utilities.types import LRSchedulerTypeTuple\n    from lightning.pytorch.utilities.model_helpers import is_overridden\n    return not isinstance(sched, LRSchedulerTypeTuple) and not is_overridden(\"lr_scheduler_step\", model)","tryCatchPattern":null,"preventionTips":["Implement lr_scheduler_step whenever using non-PyTorch schedulers","Prefer adapters that subclass torch LRScheduler"],"tags":["lr-scheduler","custom-scheduler","hook","lightning"],"backgroundTag":"missing-interface-method","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}