Lightning-AI/pytorch-lightning · error · MisconfigurationException
"`val_check_interval` should be an integer or a time-based d
Error message
"`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`."
What it means
Raised when check_val_every_n_epoch=None but val_check_interval is a float. In that mode, fractional epoch-based validation is ambiguous, so only ints or time-based durations (str 'DD:HH:MM:SS', datetime.timedelta, dict of timedelta kwargs) are accepted.
Source
Thrown at src/lightning/pytorch/trainer/connectors/data_connector.py:66
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
def prepare_data(self) -> None:
trainer = self.trainer
# on multi-gpu jobs we only want to manipulate (download, etc) on node_rank=0, local_rank=0
# or in the case where each node needs to do its own manipulation in which case just local_rank=0View on GitHub (pinned to 9fed5c27d2)
Solutions
- Use an int val_check_interval (steps within an epoch) when check_val_every_n_epoch=None
- Use a time-based duration: val_check_interval="00:30:00" or datetime.timedelta(minutes=30) or dict(hours=1, minutes=30)
- Set check_val_every_n_epoch to an int if you wanted float-style per-epoch frequency — instead express it via int interval or check_val_every_n_epoch=k
Example fix
# before trainer = Trainer(val_check_interval=0.5, check_val_every_n_epoch=None) # after import datetime trainer = Trainer(val_check_interval=100, check_val_every_n_epoch=None) # every 100 steps # or time-based trainer = Trainer(val_check_interval=datetime.timedelta(minutes=30))
Defensive patterns
Strategy: type-guard
Validate before calling
import datetime
def normalize(v):
if isinstance(v, float):
v = int(v) if v.is_integer() else None
assert not isinstance(v, float), "use int or time-based duration for val_check_interval"
return v
val_check_interval = normalize(cfg["val_check_interval"]) Type guard
def is_valid_val_check_interval(v) -> bool:
import datetime
return isinstance(v, (int, datetime.timedelta)) or isinstance(v, str) or isinstance(v, dict) Prevention
- Replace legacy float intervals (0.25 etc.) with int step counts or timedelta
- Document duration formats ('DD:HH:MM:SS', timedelta, dict kwargs) in config templates
When it happens
Trigger: Trainer(check_val_every_n_epoch=None, val_check_interval=0.5) or val_check_interval=100.0 with the default check_val_every_n_epoch; classic case is val_check_interval=0.25 copied from older configs while explicitly setting check_val_every_n_epoch=None.
Common situations: Migrating configs between Lightning versions where float semantics changed; time-based validation setups mixing duration strings with float leftovers.
Related errors
- f"`check_val_every_n_epoch` should be an integer, found {che
- f"`reload_dataloaders_every_n_epochs` should be an int >= 0,
- Device IDs (GPU/TPU) must be an int, a string, a sequence of
- {seed} is not in bounds, numpy accepts from {min_seed_value}
- Expected samples ({samples}) to be greater or equal than bat
AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28).
Data as JSON: /api/errors/ce7d8c7913ece115.
Report an issue: GitHub.