TheAlgorithms/Python · error · ValueError

graham_scan: argument must contain more than 3 points.

Error message

graham_scan: argument must contain more than 3 points.

What it means

Raised by graham_scan when the input list has 2 or fewer points, because a convex hull is undefined (a point or a segment has no hull). Note the guard is len(points) <= 2 while exactly 3 points are returned as-is, so despite the message wording the real requirement is at least 3 points.

Source

Thrown at other/graham_scan.py:115

    :return: The points on convex hell.

    Examples:
    >>> graham_scan([(9, 6), (3, 1), (0, 0), (5, 5), (5, 2), (7, 0), (3, 3), (1, 4)])
    [(0, 0), (7, 0), (9, 6), (5, 5), (1, 4)]

    >>> graham_scan([(0, 0), (1, 0), (1, 1), (0, 1)])
    [(0, 0), (1, 0), (1, 1), (0, 1)]

    >>> graham_scan([(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)])
    [(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)]

    >>> graham_scan([(-100, 20), (99, 3), (1, 10000001), (5133186, -25), (-66, -4)])
    [(5133186, -25), (1, 10000001), (-100, 20), (-66, -4)]
    """

    if len(points) <= 2:
        # There is no convex hull
        raise ValueError("graham_scan: argument must contain more than 3 points.")
    if len(points) == 3:
        return points
    # find the lowest and the most left point
    minidx = 0
    miny, minx = maxsize, maxsize
    for i, point in enumerate(points):
        x = point[0]
        y = point[1]
        if y < miny:
            miny = y
            minx = x
            minidx = i
        if y == miny and x < minx:
            minx = x
            minidx = i

    # remove the lowest and the most left point from points for preparing for sort
    points.pop(minidx)

View on GitHub (pinned to f5988cc097)

Solutions

  1. Check len(points) >= 3 before calling graham_scan and skip/short-circuit degenerate inputs
  2. Filter or assert upstream so the point set never degenerates below 3 points
  3. For exactly 3 points be aware the function returns them unsorted and without collinearity handling

Example fix

# before
hull = graham_scan(points)  # ValueError when points has <= 2 entries

# after
hull = graham_scan(points) if len(points) >= 3 else list(points)
Defensive patterns

Strategy: validation

Validate before calling

def has_hull(points) -> bool:
    return len(points) >= 3

Type guard

from typing import Sequence

def is_scannable_point_set(points: Sequence[Sequence[float]]) -> bool:
    """True when graham_scan can process the input."""
    return len(points) >= 3 and all(len(p) >= 2 for p in points)

Try / catch

try:
    hull = graham_scan(points)
except ValueError as e:
    if 'more than 3 points' in str(e):
        hull = list(points)  # degenerate set: points are their own 'hull'
    else:
        raise

Prevention

When it happens

Trigger: Calling graham_scan([(0,0)]), graham_scan([(0,0),(1,1)]), or graham_scan([]) — any list of 0, 1, or 2 (x, y) tuples.

Common situations: Running the scan over dynamically generated point clouds (clusters, filtered outliers) that can degenerate to one or two points, or feeding edge-case test data into a computational-geometry pipeline.

Related errors


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