{"record":{"id":"966abb2388c180d0","repo":"unslothai/unsloth","slug":"gradient-accumulation-steps-must-be-1","errorCode":null,"errorMessage":"gradient_accumulation_steps must be >= 1","messagePattern":"gradient_accumulation_steps must be >= 1","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1014,"sourceCode":"    # Optional explicit family override; None = detect from base_model. ``resolved_family`` is filled by normalized() with the trainer family that will run.\n    model_family: Optional[str] = None\n    resolved_family: str = \"sdxl\"\n\n    def normalized(self) -> \"DiffusionLoraConfig\":\n        \"\"\"Return a copy with derived/validated fields filled in. Raises ValueError on a\n        request that cannot train (bad numbers, or an untrainable base model).\n\n        Also coerces values that arrive as strings/blanks through the Studio config path\n        (``learning_rate`` is preserved as a string there; ``hf_token`` defaults to \"\").\"\"\"\n        resolved_family = resolve_trainable_family(self.base_model, self.model_family)\n        if self.train_steps < 1:\n            raise ValueError(\"train_steps must be >= 1\")\n        if not 0 <= int(self.num_epochs) <= 1000:\n            raise ValueError(\"num_epochs must be between 0 and 1000 (0 uses train_steps)\")\n        if self.train_batch_size < 1:\n            raise ValueError(\"train_batch_size must be >= 1\")\n        if self.gradient_accumulation_steps < 1:\n            raise ValueError(\"gradient_accumulation_steps must be >= 1\")\n        if self.lora_rank < 1:\n            raise ValueError(\"lora_rank must be >= 1\")\n        if self.lora_alpha is not None and self.lora_alpha < 1:\n            raise ValueError(\n                \"lora_alpha must be >= 1 (a zero/negative alpha scales the adapter to nothing)\"\n            )\n        if self.resolution < 64 or self.resolution % 8 != 0:\n            raise ValueError(\"resolution must be a multiple of 8 and >= 64\")\n        # A video family's VAE compresses space by 32, so an off-grid resolution changes the\n        # latent geometry silently. Refuse it here, before the GPU models are evicted.\n        if (\n            resolved_family in TRAINABLE_VIDEO_FAMILIES\n            and self.resolution % _VIDEO_RESOLUTION_MULTIPLE != 0\n        ):\n            raise ValueError(\n                f\"'{resolved_family}' trains at a resolution that is a multiple of \"\n                f\"{_VIDEO_RESOLUTION_MULTIPLE} (its VAE compresses space by that factor); \"\n                f\"got {self.resolution}.\"","sourceCodeStart":996,"sourceCodeEnd":1032,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L996-L1032","documentation":"Raised by the training-config validator before any GPU work starts: gradient_accumulation_steps was set below 1. Gradient accumulation splits a large effective batch into micro-batches, so zero or negative accumulation is meaningless and would crash or mis-scale the optimizer later in the trainer. The check exists in validation so a bad request fails cheaply, before resident GPU models are evicted.","triggerScenarios":"Submitting a training request (Studio config path or direct API call) with gradient_accumulation_steps = 0, a negative number, or a string like \"0\"/\"\" that the Studio config coercion turns into 0. Also happens when a UI form leaves the field defaulted to 0 or a computation like batch_size // micro_batch yields 0.","commonSituations":"Config files or YAML hand-edited with a 0 to 'disable' accumulation; frontend forms whose numeric input defaults to 0; arithmetic that derives accumulation steps from other values and floors to 0 for small batch sizes.","solutions":["Set gradient_accumulation_steps to 1 or higher (1 = no accumulation).","If the value arrives as a string through the Studio config path, make sure it is a non-empty numeric string that parses to >= 1.","If computing it dynamically (e.g. effective_batch / micro_batch), clamp to at least 1: max(1, computed).","Audit the request payload right before submission and fail fast in your own code with a clearer message."],"exampleFix":"# before\nconfig = TrainConfig(gradient_accumulation_steps=0)\n\n# after\nconfig = TrainConfig(gradient_accumulation_steps=1)","handlingStrategy":"validation","validationCode":"def check_gradient_accumulation_steps(v) -> int:\n    n = int(v) if v not in (None, \"\") else 1\n    if n < 1:\n        raise ValueError(f\"gradient_accumulation_steps must be >= 1, got {v!r}\")\n    return n\n\nconfig.gradient_accumulation_steps = check_gradient_accumulation_steps(config.gradient_accumulation_steps)","typeGuard":"def is_valid_gradient_accumulation_steps(v) -> bool:\n    try:\n        return int(v) >= 1\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"gradient_accumulation_steps\" in str(e):\n        config.gradient_accumulation_steps = 1\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Default numeric training fields to 1 (the minimum), never 0, in forms and templates.","Clamp any computed accumulation value with max(1, computed).","Validate the whole training config client-side before submission; every field has documented bounds in the validator's error messages."],"tags":["training","configuration","validation","diffusion","lora"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}