microsoft/qlib · error · ValueError

Unsupported earlystopping mode: {mode}

Error message

Unsupported earlystopping mode: {mode}

What it means

ValueError from `EarlyStopping.__init__` (qlib/rl/trainer/callbacks.py:112). The `mode` argument controls whether training stops when the monitored metric stops decreasing (`min`) or increasing (`max`); anything other than the exact strings 'min' or 'max' is rejected before any training starts.

Source

Thrown at qlib/rl/trainer/callbacks.py:112

        self,
        monitor: str = "reward",
        min_delta: float = 0.0,
        patience: int = 0,
        mode: Literal["min", "max"] = "max",
        baseline: float | None = None,
        restore_best_weights: bool = False,
    ):
        super().__init__()

        self.monitor = monitor
        self.patience = patience
        self.baseline = baseline
        self.min_delta = abs(min_delta)
        self.restore_best_weights = restore_best_weights
        self.best_weights: Any | None = None

        if mode not in ["min", "max"]:
            raise ValueError("Unsupported earlystopping mode: " + mode)

        if mode == "min":
            self.monitor_op = np.less
        elif mode == "max":
            self.monitor_op = np.greater

        if self.monitor_op == np.greater:
            self.min_delta *= 1
        else:
            self.min_delta *= -1

    def state_dict(self) -> dict:
        return {"wait": self.wait, "best": self.best, "best_weights": self.best_weights, "best_iter": self.best_iter}

    def load_state_dict(self, state_dict: dict) -> None:
        self.wait = state_dict["wait"]
        self.best = state_dict["best"]
        self.best_weights = state_dict["best_weights"]

View on GitHub (pinned to 79633dd950)

Solutions

  1. Set `mode` to exactly `"min"` (loss, PAW error) or `"max"` (return, reward) when constructing the callback.
  2. If migrating a Keras config with `mode: auto`, resolve it yourself: pick 'min' when monitor name contains 'loss' or 'err', else 'max'.
  3. Lowercase/validate user-supplied mode before passing: `mode = mode.lower(); assert mode in ("min", "max")`.

Example fix

// before
cb = EarlyStopping(monitor="val_loss", patience=5, mode="auto")  // Keras-style, rejected
// after
cb = EarlyStopping(monitor="val_loss", patience=5, mode="min")
Defensive patterns

Strategy: validation

Validate before calling

mode = (mode or "").lower()
assert mode in ("min", "max"), f"mode must be 'min' or 'max', got {mode!r}"

Type guard

def is_earlystopping_mode(mode) -> bool:
    return isinstance(mode, str) and mode in ("min", "max")

Try / catch

try:
    cb = EarlyStopping(monitor=m, patience=p, mode=mode)
except ValueError as e:
    if "Unsupported earlystopping mode" in str(e):
        mode = "min" if ("loss" in m or "err" in m) else "max"
        cb = EarlyStopping(monitor=m, patience=p, mode=mode)
    else:
        raise

Prevention

When it happens

Trigger: Passing `mode="MIN"` (case-sensitive), `mode="auto"` (supported by Keras but not here), `mode=0`, or None to the EarlyStopping callback constructor.

Common situations: Porting Keras/PyTorch Lightning early-stopping configs to qlib where `auto` mode or uppercase modes are accepted; YAML configs with a typo; default-None configs that assume the callback fills in a mode.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/1d56b9e16802b1ee. Report an issue: GitHub.