{"record":{"id":"e58f026eb4effe06","repo":"unslothai/unsloth","slug":"learning-rate-must-be-1-0-got-lr-r-values-t","errorCode":null,"errorMessage":"learning_rate must be < 1.0 (got {lr!r}); values that large always diverge training","messagePattern":"learning_rate must be < 1\\.0 \\(got (.+?)\\); values that large always diverge training","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"studio/backend/models/training.py","lineNumber":95,"sourceCode":"                \"access_key_id and secret_access_key\"\n            )\n        return self\n\n\ndef _parse_lr(v: Any) -> float:\n    \"\"\"Parse learning_rate as a positive float strictly below _MAX_LR_VALUE.\"\"\"\n    if v is None:\n        raise ValueError(\"learning_rate is required\")\n    if isinstance(v, bool):\n        raise ValueError(\"learning_rate must be a number, not a bool\")\n    try:\n        lr = float(v)\n    except (TypeError, ValueError):\n        raise ValueError(f\"learning_rate must be parseable as float (got {v!r})\")\n    if not (lr > 0.0):\n        raise ValueError(f\"learning_rate must be > 0 (got {lr!r}); typical range is 1e-6 .. 1e-3\")\n    if lr >= _MAX_LR_VALUE:\n        raise ValueError(\n            f\"learning_rate must be < 1.0 (got {lr!r}); values that large always diverge training\"\n        )\n    return lr\n\n\nclass TrainingStartRequest(BaseModel):\n    \"\"\"Request schema for starting training\"\"\"\n\n    model_name: str = Field(\n        ..., description = \"Model identifier (e.g., 'unsloth/llama-3-8b-bnb-4bit')\"\n    )\n    project_name: Optional[str] = Field(\n        None,\n        max_length = 80,\n        description = \"Optional user-defined project name appended to run folders and shown in history\",\n    )\n    start_request_id: Optional[str] = Field(\n        None,","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/training.py#L77-L113","documentation":"Raised by the _parse_lr parser when the learning rate is >= _MAX_LR_VALUE (1.0). Learning rates of 1.0 or more always diverge training, so the parser enforces a hard ceiling rather than letting the job start and crash the GPU worker later. The bound is strict: exactly 1.0 is rejected too.","triggerScenarios":"POST a training start request with \"learning_rate\": 1.0, 1, 5e-3 is fine but 5.0 is not — any parsed float >= 1.0, including the string \"1.0\" and the boolean-free integer 1.","commonSituations":"Unit confusion (per-100 vs per-unit schedules); AdamW-style LR copied from a paper using LR=1.0 for specific architectures; accidentally sending a percentage (e.g. 100 meaning 100%); slider UIs allowing values above 1.0 without clamping.","solutions":["Use a realistic LR in the 1e-6..1e-3 range; anything >= 1.0 will diverge.","If the number came from a config in different units, convert (e.g. per-mille 2 → 0.002).","Clamp slider/programmatic LR values in the client with min(max(lr, 1e-7), 1e-2)."],"exampleFix":"// before\nbody = { ..., learning_rate: 2 }   // meant 2e-3\n// after\nbody = { ..., learning_rate: 2e-3 }","handlingStrategy":"validation","validationCode":"MAX_LR = 1.0\n\ndef lr_under_cap(body: dict) -> bool:\n    try:\n        return float(body.get(\"learning_rate\")) < MAX_LR\n    except (TypeError, ValueError):\n        return False","typeGuard":"function lrUnderCap(v: number): boolean {\n  return v < 1.0;\n}","tryCatchPattern":null,"preventionTips":["Clamp LR sliders to 1e-7..1e-2","Double-check units when copying LRs from papers","Typical fine-tuning range is 1e-6..1e-3"],"tags":["pydantic","validation","training","hyperparameters","divergence"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}