unslothai/unsloth · error · ValueError

learning_rate must be < 1.0 (got {lr!r}); values that large

Error message

learning_rate must be < 1.0 (got {lr!r}); values that large always diverge training

What it means

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.

Source

Thrown at studio/backend/models/training.py:95

                "access_key_id and secret_access_key"
            )
        return self


def _parse_lr(v: Any) -> float:
    """Parse learning_rate as a positive float strictly below _MAX_LR_VALUE."""
    if v is None:
        raise ValueError("learning_rate is required")
    if isinstance(v, bool):
        raise ValueError("learning_rate must be a number, not a bool")
    try:
        lr = float(v)
    except (TypeError, ValueError):
        raise ValueError(f"learning_rate must be parseable as float (got {v!r})")
    if not (lr > 0.0):
        raise ValueError(f"learning_rate must be > 0 (got {lr!r}); typical range is 1e-6 .. 1e-3")
    if lr >= _MAX_LR_VALUE:
        raise ValueError(
            f"learning_rate must be < 1.0 (got {lr!r}); values that large always diverge training"
        )
    return lr


class TrainingStartRequest(BaseModel):
    """Request schema for starting training"""

    model_name: str = Field(
        ..., description = "Model identifier (e.g., 'unsloth/llama-3-8b-bnb-4bit')"
    )
    project_name: Optional[str] = Field(
        None,
        max_length = 80,
        description = "Optional user-defined project name appended to run folders and shown in history",
    )
    start_request_id: Optional[str] = Field(
        None,

View on GitHub (pinned to 203007d190)

Solutions

  1. Use a realistic LR in the 1e-6..1e-3 range; anything >= 1.0 will diverge.
  2. If the number came from a config in different units, convert (e.g. per-mille 2 → 0.002).
  3. Clamp slider/programmatic LR values in the client with min(max(lr, 1e-7), 1e-2).

Example fix

// before
body = { ..., learning_rate: 2 }   // meant 2e-3
// after
body = { ..., learning_rate: 2e-3 }
Defensive patterns

Strategy: validation

Validate before calling

MAX_LR = 1.0

def lr_under_cap(body: dict) -> bool:
    try:
        return float(body.get("learning_rate")) < MAX_LR
    except (TypeError, ValueError):
        return False

Type guard

function lrUnderCap(v: number): boolean {
  return v < 1.0;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/e58f026eb4effe06. Report an issue: GitHub.