{"record":{"id":"538ec2276e60505e","repo":"geekcomputers/Python","slug":"expected-non-negative-epoch-but-got","errorCode":null,"errorMessage":"Expected non-negative epoch, but got {}","messagePattern":"Expected non-negative epoch, but got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"ML/src/python/neuralforge/optim/schedulers.py","lineNumber":50,"sourceCode":"        self.T_i = T_0\n        super().__init__(optimizer, last_epoch)\n    \n    def get_lr(self):\n        return [\n            self.eta_min + (base_lr - self.eta_min) * (1 + math.cos(math.pi * self.T_cur / self.T_i)) / 2\n            for base_lr in self.base_lrs\n        ]\n    \n    def step(self, epoch=None):\n        if epoch is None:\n            epoch = self.last_epoch + 1\n            self.T_cur = self.T_cur + 1\n            if self.T_cur >= self.T_i:\n                self.T_cur = self.T_cur - self.T_i\n                self.T_i = self.T_i * self.T_mult\n        else:\n            if epoch < 0:\n                raise ValueError(\"Expected non-negative epoch, but got {}\".format(epoch))\n            if epoch >= self.T_0:\n                if self.T_mult == 1:\n                    self.T_cur = epoch % self.T_0\n                else:\n                    n = int(math.log((epoch / self.T_0 * (self.T_mult - 1) + 1), self.T_mult))\n                    self.T_cur = epoch - self.T_0 * (self.T_mult ** n - 1) / (self.T_mult - 1)\n                    self.T_i = self.T_0 * self.T_mult ** n\n            else:\n                self.T_i = self.T_0\n                self.T_cur = epoch\n        \n        self.last_epoch = math.floor(epoch)\n        \n        for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):\n            param_group['lr'] = lr\n\nclass OneCycleLR(_LRScheduler):\n    def __init__(self, optimizer, max_lr, total_steps, pct_start=0.3, anneal_strategy='cos',","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/geekcomputers/Python/blob/40f4cd2652d75ef8e49d76e5c4d431d458712719/ML/src/python/neuralforge/optim/schedulers.py#L32-L68","documentation":"This ValueError is raised by the cosine-annealing-with-warm-restarts scheduler's step(epoch) when an explicit negative epoch value is passed. When epoch is not None the scheduler computes the position within the annealing cycle directly from it, so a negative epoch has no meaningful interpretation and is rejected before the logarithmic restart math runs.","triggerScenarios":"Calling scheduler.step(epoch) with epoch=-1, or with a variable that can go negative such as epoch = step_count - warmup_steps before warmup completes, or epoch derived from len(loader) arithmetic that underflows early in training.","commonSituations":"Custom training loops that pass a raw global step or epoch counter that starts negative (e.g., during a warmup phase), off-by-one errors when computing epochs after resume from checkpoint, or off-by-one when epoch is derived from a zero-based loop index minus an offset.","solutions":["Pass a non-negative epoch, or call scheduler.step() with no argument to let the scheduler track epochs internally","If computing epoch = step - warmup_steps, clamp it with max(0, step - warmup_steps)","Verify checkpoint-resume logic restores the epoch counter to the correct non-negative value"],"exampleFix":"# before\nfor step, batch in enumerate(loader):\n    scheduler.step(step - warmup_steps)  # negative during warmup\n\n# after\nfor step, batch in enumerate(loader):\n    if step >= warmup_steps:\n        scheduler.step(step - warmup_steps)\n    else:\n        scheduler.step()","handlingStrategy":"validation","validationCode":"epoch = max(0, global_step - warmup_steps)\nscheduler.step(epoch)\n# or simply: scheduler.step()  # internal epoch tracking","typeGuard":null,"tryCatchPattern":"try:\n    scheduler.step(epoch)\nexcept ValueError as e:\n    if 'non-negative epoch' in str(e):\n        scheduler.step()  # fall back to internal counting\n    else:\n        raise","preventionTips":["Prefer calling scheduler.step() with no argument unless you need manual control","Clamp computed epochs with max(0, ...) during warmup phases","Test resume-from-checkpoint paths for off-by-one epoch values"],"tags":["python","pytorch","lr-scheduler","cosine-annealing","validation"],"backgroundTag":"scheduler-invalid-epoch","analyzedSha":"40f4cd2652d75ef8e49d76e5c4d431d458712719","analyzedAt":"2026-08-27T11:12:20.313Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}