{"record":{"id":"52aa66001e319920","repo":"Lightning-AI/pytorch-lightning","slug":"early-stopping-conditioned-on-metric-self-monito","errorCode":null,"errorMessage":"Early stopping conditioned on metric `{self.monitor}` which is not available. Pass in or modify your `EarlyStopping` callback to use any of the following: `{'`, `'.join(list(logs.keys()))}`","messagePattern":"Early stopping conditioned on metric `(.+?)` which is not available\\. Pass in or modify your `EarlyStopping` callback to use any of the following: `(.+?)`","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/lightning/pytorch/callbacks/early_stopping.py","lineNumber":175,"sourceCode":"    @override\n    def setup(self, trainer: \"pl.Trainer\", pl_module: \"pl.LightningModule\", stage: str) -> None:\n        if self._check_on_train_epoch_end is None:\n            # if the user runs validation multiple times per training epoch or multiple training epochs without\n            # validation, then we run after validation instead of on train epoch end\n            self._check_on_train_epoch_end = trainer.val_check_interval == 1.0 and trainer.check_val_every_n_epoch == 1\n\n    def _validate_condition_metric(self, logs: dict[str, Tensor]) -> bool:\n        monitor_val = logs.get(self.monitor)\n\n        error_msg = (\n            f\"Early stopping conditioned on metric `{self.monitor}` which is not available.\"\n            \" Pass in or modify your `EarlyStopping` callback to use any of the following:\"\n            f\" `{'`, `'.join(list(logs.keys()))}`\"\n        )\n\n        if monitor_val is None:\n            if self.strict:\n                raise RuntimeError(error_msg)\n            if self.verbose > 0:\n                rank_zero_warn(error_msg, category=RuntimeWarning)\n\n            return False\n\n        return True\n\n    @property\n    def monitor_op(self) -> Callable:\n        return self.mode_dict[self.mode]\n\n    @override\n    def state_dict(self) -> dict[str, Any]:\n        return {\n            \"wait_count\": self.wait_count,\n            \"stopped_epoch\": self.stopped_epoch,\n            \"best_score\": self.best_score,\n            \"patience\": self.patience,","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/callbacks/early_stopping.py#L157-L193","documentation":"EarlyStopping checks that the metric named by `monitor` exists in the current logged metrics (`logs`). When it is missing and `strict=True` (the default), a RuntimeError is raised telling you which keys ARE available. With `strict=False` it only warns and skips the check for that epoch.","triggerScenarios":"`EarlyStopping(monitor='val_loss')` but the LightningModule never calls `self.log('val_loss', ...)`; or the key is logged under a different name ('loss', 'val/loss'). The `_validate_condition_metric` runs inside `_run_early_stopping_check` during training/validation.","commonSituations":"Renaming the logged metric but forgetting to update the monitor string; using a step-level log key when EarlyStopping checks epoch-level aggregates; monitor metric logged only on a subset of processes or only after N epochs.","solutions":["Read the error message: it lists the available keys — pick the correct one and set it as `monitor`","Ensure `self.log(self.monitor_name, value)` is called in the module (e.g. in validation_step or training_step)","If the metric legitimately appears late, pass `check_finite=False`, `check_on_train_epoch_end` appropriately, or `strict=False` to downgrade to a warning"],"exampleFix":"# before\nEarlyStopping(monitor='val_loss')  # module logs 'loss'\n# after\nself.log('val_loss', loss, prog_bar=True)  # in validation_step\nEarlyStopping(monitor='val_loss')","handlingStrategy":"validation","validationCode":"# before fit: assert the module will log the monitor key\nmonitor = 'val_loss'\nassert hasattr(lightning_module, 'validation_step'), 'metric must come from validation'","typeGuard":null,"tryCatchPattern":"try:\n    trainer.fit(model)\nexcept RuntimeError as e:\n    if 'could not find' in str(e) or 'not available' in str(e):\n        # parse available keys from the message and fix monitor\n        raise","preventionTips":["Log every monitored metric with prog_bar=True so it appears in console/logs","Keep a single constant for the metric name used in both self.log and monitor","Run a 1-epoch smoke fit in CI to catch name mismatches early"],"tags":["lightning","early-stopping","metric-not-found","monitor"],"backgroundTag":"missing-metric-key","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}