{"record":{"id":"1d56b9e16802b1ee","repo":"microsoft/qlib","slug":"unsupported-earlystopping-mode-mode","errorCode":null,"errorMessage":"Unsupported earlystopping mode: {mode}","messagePattern":"Unsupported earlystopping mode: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/rl/trainer/callbacks.py","lineNumber":112,"sourceCode":"        self,\n        monitor: str = \"reward\",\n        min_delta: float = 0.0,\n        patience: int = 0,\n        mode: Literal[\"min\", \"max\"] = \"max\",\n        baseline: float | None = None,\n        restore_best_weights: bool = False,\n    ):\n        super().__init__()\n\n        self.monitor = monitor\n        self.patience = patience\n        self.baseline = baseline\n        self.min_delta = abs(min_delta)\n        self.restore_best_weights = restore_best_weights\n        self.best_weights: Any | None = None\n\n        if mode not in [\"min\", \"max\"]:\n            raise ValueError(\"Unsupported earlystopping mode: \" + mode)\n\n        if mode == \"min\":\n            self.monitor_op = np.less\n        elif mode == \"max\":\n            self.monitor_op = np.greater\n\n        if self.monitor_op == np.greater:\n            self.min_delta *= 1\n        else:\n            self.min_delta *= -1\n\n    def state_dict(self) -> dict:\n        return {\"wait\": self.wait, \"best\": self.best, \"best_weights\": self.best_weights, \"best_iter\": self.best_iter}\n\n    def load_state_dict(self, state_dict: dict) -> None:\n        self.wait = state_dict[\"wait\"]\n        self.best = state_dict[\"best\"]\n        self.best_weights = state_dict[\"best_weights\"]","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/rl/trainer/callbacks.py#L94-L130","documentation":"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.","triggerScenarios":"Passing `mode=\"MIN\"` (case-sensitive), `mode=\"auto\"` (supported by Keras but not here), `mode=0`, or None to the EarlyStopping callback constructor.","commonSituations":"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.","solutions":["Set `mode` to exactly `\"min\"` (loss, PAW error) or `\"max\"` (return, reward) when constructing the callback.","If migrating a Keras config with `mode: auto`, resolve it yourself: pick 'min' when monitor name contains 'loss' or 'err', else 'max'.","Lowercase/validate user-supplied mode before passing: `mode = mode.lower(); assert mode in (\"min\", \"max\")`."],"exampleFix":"// before\ncb = EarlyStopping(monitor=\"val_loss\", patience=5, mode=\"auto\")  // Keras-style, rejected\n// after\ncb = EarlyStopping(monitor=\"val_loss\", patience=5, mode=\"min\")","handlingStrategy":"validation","validationCode":"mode = (mode or \"\").lower()\nassert mode in (\"min\", \"max\"), f\"mode must be 'min' or 'max', got {mode!r}\"","typeGuard":"def is_earlystopping_mode(mode) -> bool:\n    return isinstance(mode, str) and mode in (\"min\", \"max\")","tryCatchPattern":"try:\n    cb = EarlyStopping(monitor=m, patience=p, mode=mode)\nexcept ValueError as e:\n    if \"Unsupported earlystopping mode\" in str(e):\n        mode = \"min\" if (\"loss\" in m or \"err\" in m) else \"max\"\n        cb = EarlyStopping(monitor=m, patience=p, mode=mode)\n    else:\n        raise","preventionTips":["Resolve Keras-style 'auto' mode to min/max yourself before constructing the callback.","Lowercase and validate mode in config loaders.","Remember mode is case-sensitive here, unlike some other frameworks."],"tags":["rl","trainer","callback","early-stopping","config"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}