Lightning-AI/pytorch-lightning · error · MisconfigurationException

f"`check_val_every_n_epoch` should be an integer, found

Error message

f"`check_val_every_n_epoch` should be an integer, found {check_val_every_n_epoch!r}."

What it means

MisconfigurationException from DataConnector.on_trainer_init: check_val_every_n_epoch must be an int (or None). Floats such as 1.0 and strings are rejected with strict isinstance check, even if the value is numerically integral.

Solutions

  1. Convert to int before constructing: Trainer(check_val_every_n_epoch=int(value))
  2. Set the value to None to validate every epoch (default 1.0-equivalent behavior uses interval checks)
  3. Fix the config file/YAML so the value is an unquoted integer

Example fix

# before
trainer = Trainer(check_val_every_n_epoch=1.0)
# after
trainer = Trainer(check_val_every_n_epoch=1)
Defensive patterns

Strategy: type-guard

Validate before calling

if check_val_every_n_epoch is not None:
    check_val_every_n_epoch = int(check_val_every_n_epoch)
trainer = Trainer(check_val_every_n_epoch=check_val_every_n_epoch)

Type guard

def is_valid_check_val_every_n_epoch(v) -> bool:
    return v is None or (isinstance(v, int) and not isinstance(v, bool))

Prevention

When it happens

Trigger: Trainer(check_val_every_n_epoch=1.0), or passing a config-parsed value (YAML/JSON/argparse) that arrives as float/str; np.int64 also fails the plain isinstance int check in some numpy versions.

Common situations: Hyperparameter sweeps (optuna/wandb) supplying floats; YAML configs where the value is quoted; numpy scalars from computed schedules.

Related errors


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

Appendix: source

Thrown at src/lightning/pytorch/trainer/connectors/data_connector.py:61

warning_cache = WarningCache()


class _DataConnector:
    def __init__(self, trainer: "pl.Trainer"):
        self.trainer = trainer
        self._datahook_selector: Optional[_DataHookSelector] = None

    def on_trainer_init(
        self,
        val_check_interval: Optional[Union[int, float, str, timedelta, dict]],
        reload_dataloaders_every_n_epochs: int,
        check_val_every_n_epoch: Optional[int],
    ) -> None:
        self.trainer.datamodule = None

        if check_val_every_n_epoch is not None and not isinstance(check_val_every_n_epoch, int):
            raise MisconfigurationException(
                f"`check_val_every_n_epoch` should be an integer, found {check_val_every_n_epoch!r}."
            )

        if check_val_every_n_epoch is None and isinstance(val_check_interval, float):
            raise MisconfigurationException(
                "`val_check_interval` should be an integer or a time-based duration (str 'DD:HH:MM:SS', "
                "datetime.timedelta, or dict kwargs for timedelta) when `check_val_every_n_epoch=None`."
            )

        self.trainer.check_val_every_n_epoch = check_val_every_n_epoch

        if not isinstance(reload_dataloaders_every_n_epochs, int) or (reload_dataloaders_every_n_epochs < 0):
            raise MisconfigurationException(
                f"`reload_dataloaders_every_n_epochs` should be an int >= 0, got {reload_dataloaders_every_n_epochs}."
            )

        self.trainer.reload_dataloaders_every_n_epochs = reload_dataloaders_every_n_epochs

View on GitHub (pinned to 9fed5c27d2)