{"record":{"id":"fb8c6410eac01ac5","repo":"unslothai/unsloth","slug":"learning-rate-must-be-0-got-lr-r-typical-ra","errorCode":null,"errorMessage":"learning_rate must be > 0 (got {lr!r}); typical range is 1e-6 .. 1e-3","messagePattern":"learning_rate must be > 0 \\(got (.+?)\\); typical range is 1e-6 \\.\\. 1e-3","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"studio/backend/models/training.py","lineNumber":93,"sourceCode":"            raise ValueError(\n                \"s3_config requires either use_iam_role=True or both \"\n                \"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    )","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/training.py#L75-L111","documentation":"Raised by the _parse_lr parser when the parsed learning rate is not strictly positive (zero or negative, including -0.0 and NaN comparisons falling through). A zero or negative LR makes no training progress or diverges immediately, so it is rejected at request validation time. The message suggests the typical range 1e-6 .. 1e-3.","triggerScenarios":"POST a training start request with \"learning_rate\": 0, -1e-5, \"0.0\", or a computed value that underflows to 0 (e.g. a warmup step 0 of a schedule multiplied by base LR).","commonSituations":"Warmup schedules that start at step 0 producing LR=0; subtracting constants from LR in adaptive logic; sign errors when converting units; default-initializing a numeric field to 0 in a form or proto.","solutions":["Send a positive learning rate; start with 2e-5 for LoRA or 1e-4..1e-3 for full fine-tuning and tune from there.","If computing LR from a schedule, clamp the first warmup step to a small positive epsilon or start the request at step 1.","Check sign/unit conversions in whatever computes the value."],"exampleFix":"# before (warmup step 0 -> lr 0)\nlr = base_lr * (step / warmup_steps)\nbody = {\"learning_rate\": lr}\n# after\nlr = max(base_lr * (step / warmup_steps), 1e-7)\nbody = {\"learning_rate\": lr}","handlingStrategy":"validation","validationCode":"def lr_positive(body: dict) -> bool:\n    v = body.get(\"learning_rate\")\n    try:\n        return float(v) > 0.0\n    except (TypeError, ValueError):\n        return False","typeGuard":"function lrPositive(v: number): boolean {\n  return v > 0;\n}","tryCatchPattern":null,"preventionTips":["Clamp warmup schedules to a positive epsilon at step 0","Initialize numeric form fields to 2e-5, never 0"],"tags":["pydantic","validation","training","hyperparameters","scheduling"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}