Lightning-AI/pytorch-lightning · warning

train_dataloader yielded None. If this was on purpose, ignor

Error message

train_dataloader yielded None. If this was on purpose, ignore this warning...

What it means

Training epoch loop warns when the train dataloader yields None for a batch (and dataloader_iter isn't being used). The batch is treated as absent — hooks and optimization are skipped for it.

Source

Thrown at src/lightning/pytorch/loops/training_epoch_loop.py:331

            # fetcher state so that the batch_idx is correct after restarting
            batch_idx = self.batch_idx + 1
        # Note: `is_last_batch` is not yet determined if data fetcher is a `_DataLoaderIterDataFetcher`
        self.batch_progress.is_last_batch = data_fetcher.done

        trainer = self.trainer
        if not using_dataloader_iter:
            batch = trainer.precision_plugin.convert_input(batch)
            batch = trainer.lightning_module._on_before_batch_transfer(batch, dataloader_idx=0)
            batch = call._call_strategy_hook(trainer, "batch_to_device", batch, dataloader_idx=0)

        self.batch_progress.increment_ready()
        trainer._logger_connector.on_batch_start(batch)

        batch_output: _BATCH_OUTPUTS_TYPE = None  # for mypy
        should_skip_rest_of_epoch = False

        if batch is None and not using_dataloader_iter:
            self._warning_cache.warn("train_dataloader yielded None. If this was on purpose, ignore this warning...")
        else:
            # hook
            call._call_callback_hooks(trainer, "on_train_batch_start", batch, batch_idx)
            response = call._call_lightning_module_hook(trainer, "on_train_batch_start", batch, batch_idx)
            call._call_strategy_hook(trainer, "on_train_batch_start", batch, batch_idx)
            should_skip_rest_of_epoch = response == -1
            # Signal this is the last batch for the current epoch
            if should_skip_rest_of_epoch:
                self.batch_progress.increment_by(0, is_last_batch=True)
            else:
                self.batch_progress.increment_started()

                kwargs = (
                    self._build_kwargs(OrderedDict(), batch, batch_idx)
                    if not using_dataloader_iter
                    else OrderedDict(any=dataloader_iter)
                )
                with trainer.profiler.profile("run_training_batch"):

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Filter out invalid samples in the Dataset instead of returning None
  2. Use a collate_fn that skips or replaces None items
  3. If intentional (sparse batches), ignore the warning

Example fix

# before
class DS(Dataset):
    def __getitem__(self, i):
        if bad(i):
            return None
        return x[i]
# after
class DS(Dataset):
    def __getitem__(self, i):
        if bad(i):
            return self.__getitem__(i + 1)  # or prefilter indices
        return x[i]
Defensive patterns

Strategy: validation

Validate before calling

batch = next(iter(train_dl))
assert batch is not None, 'dataloader yields None; fix dataset/collate'

Type guard

def dataset_yields_valid(ds) -> bool:
    return all(ds[i] is not None for i in range(min(5, len(ds))))

Prevention

When it happens

Trigger: A train_dataloader/dataset whose __getitem__ returns None (e.g. collate producing None, or a filter that returns None instead of skipping), or a custom iterator yielding None.

Common situations: Data cleaning code returning None for bad samples; IterableDataset with continue-style logic that still yields None.

Related errors


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