Lightning-AI/pytorch-lightning · error · MisconfigurationException

`max_epochs` must be a non-negative integer or -1. You passe

Error message

`max_epochs` must be a non-negative integer or -1. You passed in {max_epochs}.

What it means

Raised by the FitLoop constructor when the `max_epochs` argument passed to `Trainer(max_epochs=...)` is an integer strictly less than -1. Lightning treats -1 as 'infinite epochs' and 0 as a valid value handled later by fit_loop.done, so any value below -1 is rejected as a misconfiguration.

Source

Thrown at src/lightning/pytorch/loops/fit_loop.py:96

                ...
            ...

    Args:
        min_epochs: The minimum number of epochs
        max_epochs: The maximum number of epochs, can be set -1 to turn this limit off

    """

    def __init__(
        self,
        trainer: "pl.Trainer",
        min_epochs: Optional[int] = 0,
        max_epochs: Optional[int] = None,
    ) -> None:
        super().__init__(trainer)
        if isinstance(max_epochs, int) and max_epochs < -1:
            # Allow max_epochs to be zero, since this will be handled by fit_loop.done
            raise MisconfigurationException(
                f"`max_epochs` must be a non-negative integer or -1. You passed in {max_epochs}."
            )

        self.max_epochs = max_epochs
        self.min_epochs = min_epochs
        self.epoch_loop = _TrainingEpochLoop(trainer)
        self.epoch_progress = _Progress()
        self.max_batches: Union[int, float] = float("inf")

        self._data_source = _DataLoaderSource(None, "train_dataloader")
        self._combined_loader: Optional[CombinedLoader] = None
        self._combined_loader_states_to_load: list[dict[str, Any]] = []
        self._data_fetcher: Optional[_DataFetcher] = None
        self._last_train_dl_reload_epoch = float("-inf")
        self._restart_stage = RestartStage.NONE

    @property
    def total_batch_idx(self) -> int:

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Set max_epochs to a non-negative integer (e.g. 10) or -1 for infinite training
  2. Check intermediate config values: print/log max_epochs right before Trainer construction to find where it becomes < -1
  3. If parsing from CLI/config, coerce and clamp the value (e.g. `max(max_epochs, -1)` if infinite is intended)

Example fix

# before
trainer = pl.Trainer(max_epochs=n_epochs - 3)  # n_epochs=2 -> -1 ok, but n_epochs=1 -> -2 raises

# after
trainer = pl.Trainer(max_epochs=max(n_epochs - 3, 1))
Defensive patterns

Strategy: validation

Validate before calling

def safe_max_epochs(v):
    if isinstance(v, int) and v < -1:
        raise ValueError(f"max_epochs must be >= -1, got {v}")
    return v

trainer = pl.Trainer(max_epochs=safe_max_epochs(cfg['max_epochs']))

Type guard

def is_valid_max_epochs(v) -> bool:
    return not isinstance(v, int) or v >= -1

Prevention

When it happens

Trigger: Instantiating `pl.Trainer(max_epochs=-2)` or any more negative int (e.g. -5). Also occurs when max_epochs is computed from config arithmetic that underflows (e.g. `max_epochs=some_value - 3` yielding a negative result).

Common situations: Typos or off-by-one math in training scripts; YAML/JSON config files with a negated or misparsed integer; CLI flags parsed as negative numbers; conditional configs like `max_epochs=-1 if resume else epochs` where a bug produces -2 or lower.

Related errors


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