{"record":{"id":"5a77b8d6f8a12c4c","repo":"Lightning-AI/pytorch-lightning","slug":"expected-the-value-to-increase-last-last-curr","errorCode":null,"errorMessage":"Expected the value to increase, last: {last}, current: {x}","messagePattern":"Expected the value to increase, last: (.+?), current: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/lightning/fabric/utilities/throughput.py","lineNumber":710,"sourceCode":"\nclass _MonotonicWindow(list[T]):\n    \"\"\"Custom fixed size list that only supports right-append and ensures that all values increase monotonically.\"\"\"\n\n    def __init__(self, maxlen: int) -> None:\n        super().__init__()\n        self.maxlen = maxlen\n\n    @property\n    def last(self) -> Optional[T]:\n        if len(self) > 0:\n            return self[-1]\n        return None\n\n    @override\n    def append(self, x: T) -> None:\n        last = self.last\n        if last is not None and last >= x:\n            raise ValueError(f\"Expected the value to increase, last: {last}, current: {x}\")\n        list.append(self, x)\n        # truncate excess\n        if len(self) > self.maxlen:\n            del self[0]\n\n    @override\n    def __setitem__(self, key: Any, value: Any) -> None:\n        # assigning is not implemented since we don't use it. it could be by checking all previous values\n        raise NotImplementedError(\"__setitem__ is not supported\")\n","sourceCodeStart":692,"sourceCodeEnd":720,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/fabric/utilities/throughput.py#L692-L720","documentation":"_Monotonic increasing list (used to track measured elapsed time intervals for throughput estimation) enforces strictly increasing values. append() raises ValueError when the new value is <= the last recorded value, because time going backwards would corrupt the extrapolation.","triggerScenarios":"Appending a timedelta/step value that repeats or decreases, e.g. calling monitor.update() twice within the same timer resolution, or computing a duration of 0 due to time.time() truncation, or re-using a stale start timestamp after a checkpoint resume.","commonSituations":"Very fast steps where perf_counter delta rounds to the same value as before; manually calling the internal timing list; resume-from-checkpoint where the interval list is not reset; mocking time in tests.","solutions":["Ensure each append uses a freshly computed elapsed interval (time.perf_counter() delta) with enough resolution","Reset the ThroughputMonitor / timer state when resuming a run or re-running measurement in the same process","In tests or mocked environments, inject strictly increasing clock values"],"exampleFix":"# before\nintervals.append(0.0)  # repeated value from coarse clock\n\n# after\nimport time\nnow = time.perf_counter()\nintervals.append(max(now - last_timestamp, 1e-9))\nlast_timestamp = now","handlingStrategy":"validation","validationCode":"last = intervals.last\nif last is not None and value <= last:\n    value = last + 1e-9  # or skip/log instead\nintervals.append(value)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use time.perf_counter() deltas, never time.time() or integer step counters","Reset the monitor's timer state when resuming a run"],"tags":["pytorch-lightning","throughput","monotonic","timing"],"backgroundTag":"monotonicity-violation","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}