apache/beam · error · ValueError

num_retries parameter cannot exceed 10000.

Error message

num_retries parameter cannot exceed 10000.

What it means

retry.FallbackFn/WithFixedDelay-style retry guard validates its configuration in __init__. A num_retries above 10000 is rejected with ValueError to prevent effectively-infinite retry loops with unbounded cumulative backoff.

Solutions

  1. Lower num_retries to <= 10000
  2. Use stop_after_secs to bound retries by time instead of an extreme attempt count
  3. Check where the value is computed/parsed from config for a multiplication or parsing bug

Example fix

// before
retry.WithFixedDelay(1, 60, num_retries=100000)
// after
retry.WithFixedDelay(1, 60, num_retries=100, stop_after_secs=3600)
Defensive patterns

Strategy: validation

Validate before calling

if num_retries > 10000:
    raise ValueError('num_retries must be <= 10000')

Try / catch

try:
    guard = retry.WithFixedDelay(1, 60, num_retries=n)
except ValueError as e:
    logging.error('bad retry config: %s', e)
    guard = retry.WithFixedDelay(1, 60, num_retries=100)

Prevention

When it happens

Trigger: Constructing a retry guard (e.g. retry.WithFixedDelay or FallbackFn config) with num_retries > 10000, or computing num_retries from a formula that overflows the intended value.

Common situations: Typos like num_retries=100000 meaning 10000; passing 'infinite retry' by huge number instead of using stop_after_secs; unit confusion (attempts vs loops).

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/301f5653fa85bdf8. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/utils/retry.py:102

    max_delay_secs: Maximum delay (in seconds). After this limit is reached,
      further tries use max_delay_sec instead of exponentially increasing
      the time. Defaults to 1 hour.
    stop_after_secs: Places a limit on the sum of intervals returned (in
      seconds), such that the sum is <= stop_after_secs. Defaults to disabled
      (None). You may need to increase num_retries to effectively use this
      feature.
  """
  def __init__(
      self,
      initial_delay_secs,
      num_retries,
      factor=2,
      fuzz=0.5,
      max_delay_secs=60 * 60 * 1,
      stop_after_secs=None):
    self._initial_delay_secs = initial_delay_secs
    if num_retries > 10000:
      raise ValueError('num_retries parameter cannot exceed 10000.')
    self._num_retries = num_retries
    self._factor = factor
    if not 0 <= fuzz <= 1:
      raise ValueError('fuzz parameter expected to be in [0, 1] range.')
    self._fuzz = fuzz
    self._max_delay_secs = max_delay_secs
    self._stop_after_secs = stop_after_secs

  def __iter__(self):
    current_delay_secs = min(self._max_delay_secs, self._initial_delay_secs)
    total_delay_secs = 0
    for _ in range(self._num_retries):
      fuzz_multiplier = 1 - self._fuzz + random.random() * self._fuzz
      delay_secs = current_delay_secs * fuzz_multiplier
      total_delay_secs += delay_secs
      if (self._stop_after_secs is not None and
          total_delay_secs > self._stop_after_secs):
        break

View on GitHub (pinned to 12126d8942)