{"record":{"id":"c1c528ae33d45659","repo":"PaddlePaddle/PaddleOCR","slug":"expected-positive-integer-steps-per-epoch-but-got","errorCode":null,"errorMessage":"Expected positive integer steps_per_epoch, but got {}","messagePattern":"Expected positive integer steps_per_epoch, but got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"ppocr/optimizer/lr_scheduler.py","lineNumber":74,"sourceCode":"        self,\n        max_lr,\n        epochs=None,\n        steps_per_epoch=None,\n        pct_start=0.3,\n        anneal_strategy=\"cos\",\n        div_factor=25.0,\n        final_div_factor=1e4,\n        three_phase=False,\n        last_epoch=-1,\n        verbose=False,\n    ):\n        # Validate total_steps\n        if epochs <= 0 or not isinstance(epochs, int):\n            raise ValueError(\n                \"Expected positive integer epochs, but got {}\".format(epochs)\n            )\n        if steps_per_epoch <= 0 or not isinstance(steps_per_epoch, int):\n            raise ValueError(\n                \"Expected positive integer steps_per_epoch, but got {}\".format(\n                    steps_per_epoch\n                )\n            )\n        self.total_steps = epochs * steps_per_epoch\n\n        self.max_lr = max_lr\n        self.initial_lr = self.max_lr / div_factor\n        self.min_lr = self.initial_lr / final_div_factor\n\n        if three_phase:\n            self._schedule_phases = [\n                {\n                    \"end_step\": float(pct_start * self.total_steps) - 1,\n                    \"start_lr\": self.initial_lr,\n                    \"end_lr\": self.max_lr,\n                },\n                {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/ppocr/optimizer/lr_scheduler.py#L56-L92","documentation":"Companion check to error 318: the scheduler needs steps_per_epoch (iterations per epoch) as a positive integer because total_steps = epochs * steps_per_epoch drives the LR curve. A zero, negative, non-int, or missing value raises this ValueError before any training step runs.","triggerScenarios":"Calling the scheduler with steps_per_epoch=0 (e.g. num_samples//batch_size with batch > num_samples), a float, or None passed positionally.","commonSituations":"Tiny debug datasets where batch size exceeds sample count; computing steps as float division; config keys named differently (iters_per_epoch) so the parameter never gets set.","solutions":["Pass a positive int steps_per_epoch, typically math.ceil(num_samples / batch_size)","Guard: steps_per_epoch = max(1, math.ceil(num_samples / batch_size))","Check the scheduler config key name expected by PaddleOCR's optimizer builder"],"exampleFix":"# before\nsteps_per_epoch = num_samples / batch_size  # 0.0 for tiny debug set\n# after\nimport math\nsteps_per_epoch = max(1, math.ceil(num_samples / batch_size))","handlingStrategy":"validation","validationCode":"import math\nsteps_per_epoch = max(1, math.ceil(num_samples / batch_size))\nassert isinstance(steps_per_epoch, int) and steps_per_epoch > 0","typeGuard":"def valid_steps(s) -> bool:\n    return isinstance(s, int) and not isinstance(s, bool) and s > 0","tryCatchPattern":null,"preventionTips":["Compute steps with ceil division, never plain /","Clamp to >=1 for tiny debug datasets so smoke tests still run"],"tags":["paddle","lr-scheduler","config","type-error"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}