huggingface/open-r1 · error

max_penalty {max_penalty} should not be positive

Error message

max_penalty {max_penalty} should not be positive

What it means

get_repetition_penalty_reward requires max_penalty to be non-positive: it is the maximum (negative) penalty applied to repetitive completions. Passing a positive value is treated as a logic error and rejected with this ValueError before any reward computation.

Source

Thrown at src/open_r1/rewards.py:296

            rewards.append(float(reward))

        return rewards

    return cosine_scaled_reward


def get_repetition_penalty_reward(ngram_size: int, max_penalty: float, language: str = "en"):
    """
    Computes N-gram repetition penalty as described in Appendix C.2 of https://huggingface.co/papers/2502.03373.
    Reference implementation from: https://github.com/eddycmu/demystify-long-cot/blob/release/openrlhf/openrlhf/reward/repetition.py

    Args:
    ngram_size: size of the n-grams
    max_penalty: Maximum (negative) penalty for wrong answers
    language: Language of the text, defaults to `en`. Used to choose the way to split the text into n-grams.
    """
    if max_penalty > 0:
        raise ValueError(f"max_penalty {max_penalty} should not be positive")

    if language == "en":

        def zipngram(text: str, ngram_size: int):
            words = text.lower().split()
            return zip(*[words[i:] for i in range(ngram_size)]), words

    elif language == "zh":
        from transformers.utils.import_utils import _is_package_available

        if not _is_package_available("jieba"):
            raise ValueError("Please install jieba to use Chinese language")

        def zipngram(text: str, ngram_size: int):
            import jieba

            seg_list = list(jieba.cut(text))
            return zip(*[seg_list[i:] for i in range(ngram_size)]), seg_list

View on GitHub (pinned to 1416fa0cf2)

Solutions

  1. Pass a non-positive max_penalty, e.g. max_penalty=-1.0 for full repetition penalty.
  2. If you want a milder penalty, move closer to 0 from below, e.g. -0.5.
  3. Review the docstring: it is the 'Maximum (negative) penalty', so negate your intended magnitude.

Example fix

// before
reward = get_repetition_penalty_reward(ngram_size=3, max_penalty=1.0)
// after
reward = get_repetition_penalty_reward(ngram_size=3, max_penalty=-1.0)
Defensive patterns

Strategy: validation

Validate before calling

if max_penalty > 0:
    max_penalty = -abs(max_penalty)  # or reject upstream

Type guard

def is_valid_max_penalty(v) -> bool:
    return isinstance(v, (int, float)) and v <= 0

Try / catch

try:
    reward = get_repetition_penalty_reward(ngram_size=n, max_penalty=p, language=lang)
except ValueError as e:
    if "should not be positive" in str(e):
        reward = get_repetition_penalty_reward(ngram_size=n, max_penalty=-abs(p), language=lang)
    else:
        raise

Prevention

When it happens

Trigger: get_repetition_penalty_reward(ngram_size=..., max_penalty=1.0, ...) — any max_penalty > 0, e.g. 0.5 or 1.0, often from copying a 'reward weight' style positive value.

Common situations: Misunderstanding the sign convention (thinking larger positive = stronger penalty); wiring a config value meant for a different reward function; inverting a scale during refactoring.

Understand the failure class

Background: "must be positive", "Invalid value": how libraries reject invalid parameter values (ValueError, ArgumentError, INVALID_PARAMETER_VALUE) — this error's family across 28 libraries.

Related errors


AI-assisted analysis of huggingface/open-r1@1416fa0cf2 (2026-08-30). Data as JSON: /api/errors/381038f6b4c445ec. Report an issue: GitHub.