{"record":{"id":"500023777b714fda","repo":"unslothai/unsloth","slug":"penalty-has-to-be-a-positive-float-but-is-pena","errorCode":null,"errorMessage":"`penalty` has to be a positive float, but is {penalty}","messagePattern":"`penalty` has to be a positive float, but is (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/inference/inference.py","lineNumber":2321,"sourceCode":"\n    @classmethod\n    def _patch_repetition_penalty_processor(cls):\n        \"\"\"Monkey-patch transformers' RepetitionPenaltyLogitsProcessor with a\n        64-token sliding-window variant (from the OuteTTS notebook).\n        Applied once per process.\n        \"\"\"\n        if cls._repetition_penalty_patched:\n            return\n        cls._repetition_penalty_patched = True\n\n        from transformers import LogitsProcessor\n        import transformers.generation.utils as generation_utils\n\n        class RepetitionPenaltyLogitsProcessorPatch(LogitsProcessor):\n            def __init__(self, penalty: float):\n                self.penalty_last_n = 64\n                if not isinstance(penalty, float) or penalty <= 0:\n                    raise ValueError(f\"`penalty` has to be a positive float, but is {penalty}\")\n                self.penalty = penalty\n\n            @torch.no_grad()\n            def __call__(\n                self, input_ids: torch.LongTensor, scores: torch.FloatTensor\n            ) -> torch.FloatTensor:\n                if self.penalty_last_n == 0 or self.penalty == 1.0:\n                    return scores\n                batch_size, seq_len = input_ids.shape\n                vocab_size = scores.shape[-1]\n                for b in range(batch_size):\n                    start_index = max(0, seq_len - self.penalty_last_n)\n                    window_indices = input_ids[b, start_index:]\n                    if window_indices.numel() == 0:\n                        continue\n                    for token_id in set(window_indices.tolist()):\n                        if token_id >= vocab_size:\n                            continue","sourceCodeStart":2303,"sourceCodeEnd":2339,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/inference.py#L2303-L2339","documentation":"Raised inside the monkey-patch RepetitionPenaltyLogitsProcessorPatch.__init__ (inference.py:2321), installed once per process over transformers' RepetitionPenaltyLogitsProcessor with a 64-token sliding window (from the OuteTTS notebook). Unlike upstream transformers (which accepts a float >= 1.0 conceptually and lets 0 slip through), this patch strictly requires a true float > 0 — isinstance(penalty, float) — so an int (e.g. 1) or a non-positive value raises ValueError. It fires the first time a generation with repetition_penalty runs after patching.","triggerScenarios":"Passing repetition_penalty as an int (repetition_penalty=1 or 2) or a value <= 0 to audio/text generation once the patch class is instantiated; passing a numpy float or Decimal also fails the isinstance check.","commonSituations":"Config/JSON supplies repetition_penalty: 1 (int) instead of 1.0; a default of 0 used as 'unset'; copy-pasting upstream transformers code that tolerates ints; sliders in the UI yielding ints.","solutions":["Pass an explicit positive float: repetition_penalty=1.0 (int 1 fails isinstance(penalty, float)).","Coerce at the API boundary: float(repetition_penalty) before it reaches generation.","Validate range early: require 0 < repetition_penalty, and treat 1.0 as 'no penalty' (the patch short-circuits on penalty == 1.0).","If a numpy scalar is involved, wrap with float() — np.float64 is not a Python float under this check."],"exampleFix":"# before\ngenerate(text = \"hi\", repetition_penalty = 1)  # int -> ValueError\n\n# after\npenalty = float(raw_config.get(\"repetition_penalty\", 1.0))\nif penalty <= 0:\n    raise HTTPException(400, \"repetition_penalty must be positive\")\ngenerate(text = \"hi\", repetition_penalty = penalty)","handlingStrategy":"validation","validationCode":"def valid_penalty(p) -> float:\n    p = float(p)\n    if p <= 0:\n        raise ValueError(\"repetition_penalty must be > 0\")\n    return p","typeGuard":"def is_valid_penalty(p) -> bool:\n    return isinstance(p, float) and p > 0","tryCatchPattern":"try:\n    gen = engine.generate(text, repetition_penalty = penalty)\nexcept ValueError as e:\n    if \"penalty\" in str(e):\n        return HTTPException(400, \"repetition_penalty must be a positive float\")\n    raise","preventionTips":["Always coerce repetition_penalty with float() at the API boundary.","Reject non-positive values before generation; use 1.0 to mean 'no penalty'.","Beware numpy scalars and ints from JSON — both fail the isinstance(penalty, float) check.","Validate ranges in the UI/slider so ints never reach the engine."],"tags":["audio","sampling","repetition-penalty","validation","transformers"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}