{"record":{"id":"ce7d8c7913ece115","repo":"Lightning-AI/pytorch-lightning","slug":"val-check-interval-should-be-an-integer-or-a-ti","errorCode":null,"errorMessage":"\"`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`.\"","messagePattern":"\"`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`\\.\"","errorType":"validation","errorClass":"MisconfigurationException","httpStatus":null,"severity":"error","filePath":"src/lightning/pytorch/trainer/connectors/data_connector.py","lineNumber":66,"sourceCode":"    def __init__(self, trainer: \"pl.Trainer\"):\n        self.trainer = trainer\n        self._datahook_selector: Optional[_DataHookSelector] = None\n\n    def on_trainer_init(\n        self,\n        val_check_interval: Optional[Union[int, float, str, timedelta, dict]],\n        reload_dataloaders_every_n_epochs: int,\n        check_val_every_n_epoch: Optional[int],\n    ) -> None:\n        self.trainer.datamodule = None\n\n        if check_val_every_n_epoch is not None and not isinstance(check_val_every_n_epoch, int):\n            raise MisconfigurationException(\n                f\"`check_val_every_n_epoch` should be an integer, found {check_val_every_n_epoch!r}.\"\n            )\n\n        if check_val_every_n_epoch is None and isinstance(val_check_interval, float):\n            raise MisconfigurationException(\n                \"`val_check_interval` should be an integer or a time-based duration (str 'DD:HH:MM:SS', \"\n                \"datetime.timedelta, or dict kwargs for timedelta) when `check_val_every_n_epoch=None`.\"\n            )\n\n        self.trainer.check_val_every_n_epoch = check_val_every_n_epoch\n\n        if not isinstance(reload_dataloaders_every_n_epochs, int) or (reload_dataloaders_every_n_epochs < 0):\n            raise MisconfigurationException(\n                f\"`reload_dataloaders_every_n_epochs` should be an int >= 0, got {reload_dataloaders_every_n_epochs}.\"\n            )\n\n        self.trainer.reload_dataloaders_every_n_epochs = reload_dataloaders_every_n_epochs\n\n    def prepare_data(self) -> None:\n        trainer = self.trainer\n\n        # on multi-gpu jobs we only want to manipulate (download, etc) on node_rank=0, local_rank=0\n        # or in the case where each node needs to do its own manipulation in which case just local_rank=0","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/trainer/connectors/data_connector.py#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"Migrating configs between Lightning versions where float semantics changed; time-based validation setups mixing duration strings with float leftovers.","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"],"exampleFix":"# before\ntrainer = Trainer(val_check_interval=0.5, check_val_every_n_epoch=None)\n# after\nimport datetime\ntrainer = Trainer(val_check_interval=100, check_val_every_n_epoch=None)  # every 100 steps\n# or time-based\ntrainer = Trainer(val_check_interval=datetime.timedelta(minutes=30))","handlingStrategy":"type-guard","validationCode":"import datetime\ndef normalize(v):\n    if isinstance(v, float):\n        v = int(v) if v.is_integer() else None\n    assert not isinstance(v, float), \"use int or time-based duration for val_check_interval\"\n    return v\nval_check_interval = normalize(cfg[\"val_check_interval\"])","typeGuard":"def is_valid_val_check_interval(v) -> bool:\n    import datetime\n    return isinstance(v, (int, datetime.timedelta)) or isinstance(v, str) or isinstance(v, dict)","tryCatchPattern":null,"preventionTips":["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"],"tags":["lightning","val-check-interval","validation","trainer-init","config"],"backgroundTag":"trainer-config-type-validation","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}