huggingface/transformers · error · ValueError

`penalty` has to be a strictly positive float, but is {penal

Error message

`penalty` has to be a strictly positive float, but is {penalty}

What it means

Thrown by RepetitionPenaltyLogitsProcessor.__init__ when penalty is not a Python float or is <= 0. The penalty multiplies/divides logits of already-seen tokens, so a non-positive value has no valid meaning. The strict isinstance(penalty, float) check rejects ints even if positive (e.g. penalty=1 as int fails, 1.0 passes).

Source

Thrown at src/transformers/generation/logits_process.py:360

    I'm not going to be able to do that. I'll just have to go out and play

    >>> # We can also exclude the input prompt by creating an instance of this class
    >>> # with a `prompt_ignore_length` and passing it as a custom logit processor
    >>> rep_pen_processor = RepetitionPenaltyLogitsProcessor(
    ...     penalty=1.1,
    ...     prompt_ignore_length=inputs["input_ids"].shape[-1]
    ... )
    >>> penalized_ids = model.generate(**inputs, logits_processor=[rep_pen_processor])
    >>> print(tokenizer.batch_decode(penalized_ids, skip_special_tokens=True)[0])
    I'm not going to be able to do that. I'm going to have to go through a lot of things, and
    ```
    """

    supports_continuous_batching = False

    def __init__(self, penalty: float, prompt_ignore_length: int | None = None):
        if not isinstance(penalty, float) or not (penalty > 0):
            raise ValueError(f"`penalty` has to be a strictly positive float, but is {penalty}")

        if prompt_ignore_length is not None and (
            not isinstance(prompt_ignore_length, int) or prompt_ignore_length < 0
        ):
            raise ValueError(f"`prompt_ignore_length` has to be a positive integer, but is {prompt_ignore_length}")

        self.penalty = penalty
        self.prompt_ignore_length = prompt_ignore_length
        self.logits_indices = None
        self.cu_seq_lens_q = None

    @add_start_docstrings(LOGITS_PROCESSOR_INPUTS_DOCSTRING)
    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
        if self.prompt_ignore_length:
            input_ids = input_ids[:, self.prompt_ignore_length :]

        if scores.dim() == 3:
            if self.logits_indices is not None and self.cu_seq_lens_q is not None:

View on GitHub (pinned to a597f97485)

Solutions

  1. Pass a positive float literal: RepetitionPenaltyLogitsProcessor(1.2)
  2. Wrap config-sourced values: RepetitionPenaltyLogitsProcessor(float(penalty)) after asserting penalty > 0
  3. If penalty == 1.0, skip adding the processor entirely — it is a mathematical no-op

Example fix

# before
proc = RepetitionPenaltyLogitsProcessor(1)  # int -> ValueError

# after
proc = RepetitionPenaltyLogitsProcessor(1.0)
# or skip when neutral:
procs = [] if penalty == 1 else [RepetitionPenaltyLogitsProcessor(float(penalty))]
Defensive patterns

Strategy: validation

Validate before calling

def valid_penalty(p):
    return isinstance(p, float) and p > 0.0

Type guard

def is_valid_penalty(p) -> bool:
    return type(p) is float and p > 0.0

Try / catch

try:
    proc = RepetitionPenaltyLogitsProcessor(float(p))
except ValueError as e:
    raise ValueError(f'Bad repetition_penalty={p!r}: {e}') from e

Prevention

When it happens

Trigger: RepetitionPenaltyLogitsProcessor(1) with an int (isinstance check fails); penalty=0.0 or a negative float; building this processor indirectly via model.generate(repetition_penalty=...) after generation config loads an int-typed value.

Common situations: Config files that store repetition_penalty: 1 (int) which some YAML loaders keep as int; intending penalty=1.0 (a no-op) but writing it as int; programmatic sweeps that step penalty in numpy int or int values.

Related errors


AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14). Data as JSON: /api/errors/ebfbdcba80914af6. Report an issue: GitHub.