{"record":{"id":"5804e5b8faae4e12","repo":"unslothai/unsloth","slug":"learning-rate-must-be-a-number-not-a-bool","errorCode":null,"errorMessage":"learning_rate must be a number, not a bool","messagePattern":"learning_rate must be a number, not a bool","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"studio/backend/models/training.py","lineNumber":87,"sourceCode":"    )\n\n    @model_validator(mode = \"after\")\n    def _check_credentials(self) -> \"S3Config\":\n        # Require either IAM role auth or a full key pair so credentials are never half-configured.\n        if not self.use_iam_role and not (self.access_key_id and self.secret_access_key):\n            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')\"","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/training.py#L69-L105","documentation":"Raised by the _parse_lr parser: in Python, bool is a subclass of int, so True/False would otherwise parse as float 1.0/0.0 and slip through numeric validation. The parser explicitly rejects bools before the float() conversion so that a boolean is never silently coerced into a learning rate (1.0 would also be caught by the < 1.0 cap, but False would parse as 0.0 and only fail the > 0 check with a misleading message).","triggerScenarios":"POST a training start request with \"learning_rate\": true or \"learning_rate\": false — e.g. a YAML/JSON config where the LR was templated from a flag, or a UI checkbox bound to the wrong field.","commonSituations":"Config templating (Helm/Jinja) that renders a boolean into a numeric slot; YAML configs where `learning_rate: yes` parses as boolean true; feature-flag lookups accidentally wired to the LR field; dynamically built request dicts with wrong keys.","solutions":["Replace the boolean with a real numeric learning rate, e.g. 2e-5.","If YAML is the source, quote the value (learning_rate: \"2e-5\") to avoid YAML 1.1 truthy parsing of yes/no/on/off.","Add a client-side typeof check (number, not boolean) before submitting."],"exampleFix":"# config.yaml before\nlearning_rate: yes   # YAML parses as boolean True\n# after\nlearning_rate: 2.0e-05","handlingStrategy":"type-guard","validationCode":"def lr_is_numeric(body: dict) -> bool:\n    return not isinstance(body.get(\"learning_rate\"), bool)","typeGuard":"function isNumberNotBool(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v);\n}","tryCatchPattern":null,"preventionTips":["Quote YAML learning_rate values","Never bind a boolean/flag input to the LR field","Add a schema check (typeof number) in the request builder"],"tags":["pydantic","validation","training","yaml","type-coercion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}