TheAlgorithms/Python · error · ValueError

Limit for the Catalan sequence must be ≥ 0

Error message

Limit for the Catalan sequence must be ≥ 0

What it means

Raised by catalan_numbers(upper_limit) when upper_limit is negative. The function builds a list of length upper_limit + 1, so any negative limit other than the guard would create an empty or invalid list; a negative index would crash later. ValueError is thrown immediately with a clear 'must be >= 0' message (documented in the doctest for -1).

Source

Thrown at dynamic_programming/catalan_numbers.py:43

 *  [1] https://brilliant.org/wiki/catalan-numbers/
 *  [2] https://en.wikipedia.org/wiki/Catalan_number
"""


def catalan_numbers(upper_limit: int) -> "list[int]":
    """
    Return a list of the Catalan number sequence from 0 through `upper_limit`.

    >>> catalan_numbers(5)
    [1, 1, 2, 5, 14, 42]
    >>> catalan_numbers(2)
    [1, 1, 2]
    >>> catalan_numbers(-1)
    Traceback (most recent call last):
    ValueError: Limit for the Catalan sequence must be ≥ 0
    """
    if upper_limit < 0:
        raise ValueError("Limit for the Catalan sequence must be ≥ 0")

    catalan_list = [0] * (upper_limit + 1)

    # Base case: C(0) = C(1) = 1
    catalan_list[0] = 1
    if upper_limit > 0:
        catalan_list[1] = 1

    # Recurrence relation: C(i) = sum(C(j).C(i-j-1)), from j = 0 to i
    for i in range(2, upper_limit + 1):
        for j in range(i):
            catalan_list[i] += catalan_list[j] * catalan_list[i - j - 1]

    return catalan_list


if __name__ == "__main__":
    print("\n********* Catalan Numbers Using Dynamic Programming ************\n")

View on GitHub (pinned to f5988cc097)

Solutions

  1. Check the bound before calling: n = max(0, n) only if clamping is acceptable, otherwise surface the error to the caller.
  2. Fix the source of the negative value — usually an empty input list or an off-by-one in limit computation.
  3. Validate user-supplied limits at the CLI/config boundary with int(x); x >= 0 checks.

Example fix

# before
limit = len(values) - 1  # -1 when values is empty
cats = catalan_numbers(limit)

# after
if values:
    cats = catalan_numbers(len(values) - 1)
else:
    cats = []
Defensive patterns

Strategy: validation

Validate before calling

upper_limit = int(upper_limit)
if upper_limit < 0:
    raise ValueError('upper_limit must be >= 0')
cats = catalan_numbers(upper_limit)

Type guard

def is_non_negative_int(value: object) -> bool:
    return isinstance(value, int) and value >= 0

Try / catch

try:
    cats = catalan_numbers(limit)
except ValueError as exc:
    if 'must be' in str(exc):
        cats = []  # or re-raise with domain context
    else:
        raise

Prevention

When it happens

Trigger: catalan_numbers(-1) or any negative integer argument, e.g. when upper_limit comes from user input or len(data) - 1 on an empty dataset. Floats like -0.5 also compare negative and raise.

Common situations: Computing a range bound from an empty collection (len([]) - 1 == -1); CLI arguments parsed without validation; off-by-one when converting an inclusive/exclusive limit.

Related errors


AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14). Data as JSON: /api/errors/8e859f257272446d. Report an issue: GitHub.