{"record":{"id":"5431eb12db90cc27","repo":"Lightning-AI/pytorch-lightning","slug":"reducelronplateau-conditioned-on-metric-monitor-k","errorCode":null,"errorMessage":"ReduceLROnPlateau conditioned on metric {monitor_key} which is not available. Available metrics are: {avail_metrics}. Condition can be set using `monitor` key in lr scheduler dict","messagePattern":"ReduceLROnPlateau conditioned on metric (.+?) which is not available\\. Available metrics are: (.+?)\\. Condition can be set using `monitor` key in lr scheduler dict","errorType":"exception","errorClass":"MisconfigurationException","httpStatus":null,"severity":"error","filePath":"src/lightning/pytorch/loops/training_epoch_loop.py","lineNumber":495,"sourceCode":"\n        for config in trainer.lr_scheduler_configs:\n            if update_plateau_schedulers ^ config.reduce_on_plateau:\n                continue\n\n            current_idx = self.batch_idx if interval == \"step\" else trainer.current_epoch\n            current_idx += 1  # account for both batch and epoch starts from 0\n            # Take step if call to update_learning_rates matches the interval key and\n            # the current step modulo the schedulers frequency is zero\n            if config.interval == interval and current_idx % config.frequency == 0:\n                monitor_val = None\n                if config.reduce_on_plateau:\n                    monitor_key = config.monitor\n                    assert monitor_key is not None\n                    monitor_val = self._get_monitor_value(monitor_key)\n                    if monitor_val is None:\n                        if config.strict:\n                            avail_metrics = list(trainer.callback_metrics)\n                            raise MisconfigurationException(\n                                f\"ReduceLROnPlateau conditioned on metric {monitor_key}\"\n                                f\" which is not available. Available metrics are: {avail_metrics}.\"\n                                \" Condition can be set using `monitor` key in lr scheduler dict\"\n                            )\n                        rank_zero_warn(\n                            f\"ReduceLROnPlateau conditioned on metric {monitor_key}\"\n                            \" which is not available but strict is set to `False`.\"\n                            \" Skipping learning rate update.\",\n                            category=RuntimeWarning,\n                        )\n                        continue\n\n                self.scheduler_progress.increment_ready()\n\n                # update LR\n                call._call_lightning_module_hook(\n                    trainer,\n                    \"lr_scheduler_step\",","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/loops/training_epoch_loop.py#L477-L513","documentation":"Raised in _update_learning_rates when a ReduceLROnPlateau scheduler is configured (strict=True by default) with a `monitor` metric that is absent from trainer.callback_metrics at epoch end. ReduceLROnPlateau needs the monitored value each epoch to decide whether to reduce LR; without it the scheduler cannot operate.","triggerScenarios":"`configure_optimizers` returning `{'scheduler': torch.optim.lr_scheduler.ReduceLROnPlateau(opt), 'monitor': 'val_loss'}` while `validation_step`/`training_step` never calls `self.log('val_loss', ...)`; monitor name typo like 'val/loss' vs 'val_loss'; metric logged only under a condition that skips.","commonSituations":"Renaming logged metrics without updating the scheduler dict; running with limit_val_batches=0 so the val metric is never produced; early in training when the metric is logged with on_epoch=False only.","solutions":["Log the monitored metric: `self.log('val_loss', loss, prog_bar=True)` (ensure it lands in callback_metrics, i.e. epoch-level aggregation)","Fix the monitor string to exactly match the logged metric name","If absence is acceptable, set `strict=False` in the scheduler dict to downgrade to a warning"],"exampleFix":"# before\n# in configure_optimizers\nreturn {'optimizer': opt, 'scheduler': ReduceLROnPlateau(opt), 'monitor': 'val_f1'}\n# validation_step only logs 'val_loss'\n\n# after\ndef validation_step(self, batch, batch_idx):\n    loss, f1 = self._step(batch)\n    self.log('val_f1', f1, on_epoch=True, prog_bar=True)\n    return loss","handlingStrategy":"validation","validationCode":"# in configure_optimizers or LightningModule setup\nmonitor = 'val_loss'\nlogged = {'val_loss'}  # metrics you reliably self.log() with on_epoch aggregation\nassert monitor in logged or not strict, f'{monitor} not logged'","typeGuard":"def monitor_available(monitor: str, model) -> bool:\n    return monitor in model.trainer.callback_metrics if model.trainer else monitor in model._logged_metric_names","tryCatchPattern":"try:\n    trainer.fit(model, datamodule=dm)\nexcept MisconfigurationException as e:\n    if 'ReduceLROnPlateau' in str(e):\n        raise ValueError(f\"monitor metric missing: verify self.log('{monitor}', ...) exists\") from e\n    raise","preventionTips":["Keep a single constant for the monitor name used in both self.log and the scheduler dict","Ensure the metric is logged every validation epoch with default on_epoch aggregation","Use strict=False only when intentionally tolerating missing metrics"],"tags":["pytorch-lightning","lr-scheduler","reducelronplateau","monitor-metric","logging"],"backgroundTag":"scheduler-monitor-metric-missing","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}