{"record":{"id":"b445f23de252f5cf","repo":"unslothai/unsloth","slug":"resolution-must-be-a-multiple-of-8-and-64","errorCode":null,"errorMessage":"resolution must be a multiple of 8 and >= 64","messagePattern":"resolution must be a multiple of 8 and >= 64","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1022,"sourceCode":"        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}.\"\n            )\n        if self.mixed_precision not in (\"bf16\", \"fp16\", \"no\"):\n            raise ValueError(\"mixed_precision must be one of bf16 / fp16 / no\")\n        # torch.manual_seed unpacks int64/uint64, so anything wider raises inside the trainer, after eviction. Catch it here.\n        if not -(2**63) <= int(self.seed) <= 2**64 - 1:\n            raise ValueError(\"seed must fit in torch's 64-bit range\")\n        # Refuse fp16 for a bf16-only DiT family up front, before evicting resident models.\n        if self.mixed_precision == \"fp16\" and resolved_family in _FORCE_BF16_FAMILIES:","sourceCodeStart":1004,"sourceCodeEnd":1040,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L1004-L1040","documentation":"The validator rejected a training resolution below 64 or not divisible by 8. Diffusion VAEs downsample by a power of two, so an off-grid resolution changes latent geometry (cropping/padding) silently, and sub-64px images are too small for the patch/latent structure to be valid. The check runs in validation, before GPU memory is touched.","triggerScenarios":"A training request with resolution like 100, 500, 63, or 48 — anything < 64 or not a multiple of 8. Typically from free-form numeric fields, downscale experiments, or configs generated from arbitrary image dimensions.","commonSituations":"Matching resolution to a dataset's native size (e.g. 512x384 crops work, but 500 does not); low-VRAM users trying very small resolutions like 32; typo'd values.","solutions":["Round the resolution to the nearest multiple of 8 that is >= 64 (e.g. 500 -> 504 or 512).","Use standard buckets: 512, 768, 1024 for image training.","If shrinking for VRAM, do not go below 64; reduce batch size or resolution tier instead."],"exampleFix":"# before\nconfig = TrainConfig(resolution=500)\n\n# after\nconfig = TrainConfig(resolution=512)","handlingStrategy":"validation","validationCode":"def check_resolution(v) -> int:\n    r = int(v)\n    if r < 64 or r % 8 != 0:\n        raise ValueError(f\"resolution must be a multiple of 8 and >= 64, got {r}\")\n    return r\n\ndef snap_resolution(v) -> int:\n    return max(64, (int(v) // 8) * 8)","typeGuard":"def is_valid_resolution(v) -> bool:\n    try:\n        r = int(v)\n        return r >= 64 and r % 8 == 0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"resolution\" in str(e) and \"multiple of 8\" in str(e):\n        config.resolution = snap_resolution(config.resolution)\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Snap any dataset-derived resolution to the 8-grid before submitting: max(64, (r // 8) * 8).","Use standard buckets (512/768/1024) in templates.","For video families, apply the stricter 32-grid check instead (see the follow-on error)."],"tags":["training","resolution","configuration","validation","diffusion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}