Lightning-AI/pytorch-lightning · error · RuntimeError

Early stopping conditioned on metric `{self.monitor}` which

Error message

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()))}`

What it means

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.

Source

Thrown at src/lightning/pytorch/callbacks/early_stopping.py:175

    @override
    def setup(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", stage: str) -> None:
        if self._check_on_train_epoch_end is None:
            # if the user runs validation multiple times per training epoch or multiple training epochs without
            # validation, then we run after validation instead of on train epoch end
            self._check_on_train_epoch_end = trainer.val_check_interval == 1.0 and trainer.check_val_every_n_epoch == 1

    def _validate_condition_metric(self, logs: dict[str, Tensor]) -> bool:
        monitor_val = logs.get(self.monitor)

        error_msg = (
            f"Early stopping conditioned on metric `{self.monitor}` which is not available."
            " Pass in or modify your `EarlyStopping` callback to use any of the following:"
            f" `{'`, `'.join(list(logs.keys()))}`"
        )

        if monitor_val is None:
            if self.strict:
                raise RuntimeError(error_msg)
            if self.verbose > 0:
                rank_zero_warn(error_msg, category=RuntimeWarning)

            return False

        return True

    @property
    def monitor_op(self) -> Callable:
        return self.mode_dict[self.mode]

    @override
    def state_dict(self) -> dict[str, Any]:
        return {
            "wait_count": self.wait_count,
            "stopped_epoch": self.stopped_epoch,
            "best_score": self.best_score,
            "patience": self.patience,

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Read the error message: it lists the available keys — pick the correct one and set it as `monitor`
  2. Ensure `self.log(self.monitor_name, value)` is called in the module (e.g. in validation_step or training_step)
  3. If the metric legitimately appears late, pass `check_finite=False`, `check_on_train_epoch_end` appropriately, or `strict=False` to downgrade to a warning

Example fix

# before
EarlyStopping(monitor='val_loss')  # module logs 'loss'
# after
self.log('val_loss', loss, prog_bar=True)  # in validation_step
EarlyStopping(monitor='val_loss')
Defensive patterns

Strategy: validation

Validate before calling

# before fit: assert the module will log the monitor key
monitor = 'val_loss'
assert hasattr(lightning_module, 'validation_step'), 'metric must come from validation'

Try / catch

try:
    trainer.fit(model)
except RuntimeError as e:
    if 'could not find' in str(e) or 'not available' in str(e):
        # parse available keys from the message and fix monitor
        raise

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28). Data as JSON: /api/errors/52aa66001e319920. Report an issue: GitHub.