{"record":{"id":"fbf535876a79998e","repo":"Lightning-AI/pytorch-lightning","slug":"invalid-mode-has-to-be-min-or-max-found-self-mo","errorCode":null,"errorMessage":"Invalid mode. Has to be min or max, found {self.mode}","messagePattern":"Invalid mode\\. Has to be min or max, found (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/lightning/fabric/utilities/spike.py","lineNumber":140,"sourceCode":"\n            with open(self.exclude_batches_path, \"w\") as f:\n                json.dump(self.bad_batches, f, indent=4)\n\n        raise TrainingSpikeException(batch_idx=batch_idx)\n\n    def _check_atol(self, val_a: Union[float, torch.Tensor], val_b: Union[float, torch.Tensor]) -> bool:\n        return (self.atol is None) or bool(abs(val_a - val_b) >= abs(self.atol))  # type: ignore\n\n    def _check_rtol(self, val_a: Union[float, torch.Tensor], val_b: Union[float, torch.Tensor]) -> bool:\n        return (self.rtol is None) or bool(abs(val_a - val_b) >= abs(self.rtol * val_b))  # type: ignore\n\n    def _is_better(self, diff_val: torch.Tensor) -> bool:\n        if self.mode == \"min\":\n            return bool((diff_val <= 0.0).all())\n        if self.mode == \"max\":\n            return bool((diff_val >= 0).all())\n\n        raise ValueError(f\"Invalid mode. Has to be min or max, found {self.mode}\")\n\n    def _update_stats(self, val: torch.Tensor) -> None:\n        # only update if finite\n        self.running_mean.update(val)\n        self.last_val = val\n\n    def state_dict(self) -> dict[str, Any]:\n        return {\n            \"last_val\": self.last_val.item() if isinstance(self.last_val, torch.Tensor) else self.last_val,\n            \"mode\": self.mode,\n            \"warmup\": self.warmup,\n            \"atol\": self.atol,\n            \"rtol\": self.rtol,\n            \"bad_batches\": self.bad_batches,\n            \"bad_batches_path\": self.exclude_batches_path,\n            \"running\": self.running_mean.state_dict(),\n            \"mean\": self.running_mean.base_metric.state_dict(),\n        }","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/fabric/utilities/spike.py#L122-L158","documentation":"`SpikeDetection._is_better` compares the current metric against the running mean using directionality given by `mode`, which must be \"min\" or \"max\". If mode is anything else (typo, wrong case, None), it raises ValueError listing the offending mode.","triggerScenarios":"Constructing `SpikeDetection(mode=\"minimum\")`, `mode=\"MAX\"`, `mode=None`, or mutating `self.mode` after init; any value other than exactly \"min\"/\"max\" hits the raise when a spike check is evaluated.","commonSituations":"Config files using verbose values like \"minimize\"/\"maximize\" (common with other libraries' `mode` conventions), YAML casing issues, copying configs from early-stopping setups that accept different strings.","solutions":["Set mode to exactly \"min\" or \"max\" (lowercase)","If using \"minimize\"/\"maximize\" style configs, map them: `{\"minimize\": \"min\", \"maximize\": \"max\"}[cfg.mode]`","Add a config assertion at startup so invalid modes fail fast"],"exampleFix":"# before\nSpikeDetection(mode=\"minimize\")  # or \"MAX\"\n\n# after\nSpikeDetection(mode=\"min\")  # loss; use \"max\" for accuracy-style metrics","handlingStrategy":"validation","validationCode":"mode = {\"minimize\": \"min\", \"maximize\": \"max\"}.get(mode, mode)\nassert mode in (\"min\", \"max\"), f\"bad mode: {mode}\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use exactly \"min\"/\"max\" lowercase","Validate metric direction strings from config files at startup"],"tags":["lightning","spike-detection","configuration"],"backgroundTag":"invalid-enum-value","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}