unslothai/unsloth · error · ValueError

Unsloth MLX: max_grad_leaf_norm={max_grad_leaf_norm} must be

Error message

Unsloth MLX: max_grad_leaf_norm={max_grad_leaf_norm} must be finite and >= 0 (0 or None disables proportional leaf-norm clipping).

What it means

Raised by the Unsloth MLX training worker when config['max_grad_leaf_norm'] is negative or non-finite. This setting controls proportional leaf-norm gradient clipping in the MLX trainer; 0 or None disables it, and any other value must be a finite float >= 0. Like max_grad_value it is re-validated in the worker because direct worker callers bypass training.py's normalization.

Source

Thrown at studio/backend/core/training/worker.py:3022

        eval_steps_val = max(1, int(eval_steps_value * max_steps))
    else:
        eval_steps_val = int(eval_steps_value)

    # Re-validate for direct worker callers; training.py normalizes the main path.
    max_grad_norm = _resolve_mlx_max_grad_norm(config.get("max_grad_norm"))
    max_grad_value = config.get("max_grad_value")
    if max_grad_value is not None:
        max_grad_value = float(max_grad_value)
        if max_grad_value < 0 or not math.isfinite(max_grad_value):
            raise ValueError(
                f"Unsloth MLX: max_grad_value={max_grad_value} must be finite and >= 0 "
                "(0 or None disables elementwise clipping)."
            )
    max_grad_leaf_norm = config.get("max_grad_leaf_norm")
    if max_grad_leaf_norm is not None:
        max_grad_leaf_norm = float(max_grad_leaf_norm)
        if max_grad_leaf_norm < 0 or not math.isfinite(max_grad_leaf_norm):
            raise ValueError(
                f"Unsloth MLX: max_grad_leaf_norm={max_grad_leaf_norm} must be finite and >= 0 "
                "(0 or None disables proportional leaf-norm clipping)."
            )
    weight_decay = config.get("weight_decay", 0.001)
    weight_decay = 0.001 if weight_decay is None else float(weight_decay)

    mlx_config_kwargs = dict(
        per_device_train_batch_size = batch_size,
        gradient_accumulation_steps = grad_accum,
        max_steps = max_steps,
        learning_rate = lr_value,
        warmup_steps = warmup_steps,
        lr_scheduler_type = lr_scheduler_type,
        optim = optim_name,
        weight_decay = weight_decay,
        max_grad_norm = max_grad_norm,
        max_grad_value = max_grad_value,
        logging_steps = 1,

View on GitHub (pinned to 203007d190)

Solutions

  1. Set max_grad_leaf_norm to 0 or omit it (None) to disable proportional leaf-norm clipping, or use a finite positive float such as 1.0.
  2. Validate/normalize the value at the config boundary (API/UI) before it reaches the worker.
  3. Audit the config source for NaN/negative sentinels if the value is computed.

Example fix

# before
config = {"max_grad_leaf_norm": float("nan")}

# after
import math
value = config.get("max_grad_leaf_norm")
config["max_grad_leaf_norm"] = value if (value is not None and math.isfinite(value) and value >= 0) else None
Defensive patterns

Strategy: validation

Validate before calling

import math

value = config.get("max_grad_leaf_norm")
if value is not None and (not isinstance(value, (int, float)) or not math.isfinite(value) or value < 0):
    config["max_grad_leaf_norm"] = None

Type guard

def is_valid_max_grad_leaf_norm(v) -> bool:
    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and v >= 0)

Try / catch

try:
    run_training(config)
except ValueError as e:
    if "max_grad_leaf_norm" in str(e):
        config["max_grad_leaf_norm"] = None
        run_training(config)
    else:
        raise

Prevention

When it happens

Trigger: Calling the MLX training worker with config['max_grad_leaf_norm'] set to a negative number, NaN, or +/-inf. Finite positive values, 0, and None pass validation.

Common situations: Copying a config from another trainer where -1 means 'disabled'; NaN leaking in from a YAML .nan entry or a divide-by-zero in a sweep; a frontend sending an unvalidated numeric input.

Related errors


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