{"record":{"id":"00fbd7fade309d0a","repo":"TheAlgorithms/Python","slug":"graham-scan-argument-must-contain-more-than-3-poi","errorCode":null,"errorMessage":"graham_scan: argument must contain more than 3 points.","messagePattern":"graham_scan: argument must contain more than 3 points\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"other/graham_scan.py","lineNumber":115,"sourceCode":"    :return: The points on convex hell.\n\n    Examples:\n    >>> graham_scan([(9, 6), (3, 1), (0, 0), (5, 5), (5, 2), (7, 0), (3, 3), (1, 4)])\n    [(0, 0), (7, 0), (9, 6), (5, 5), (1, 4)]\n\n    >>> graham_scan([(0, 0), (1, 0), (1, 1), (0, 1)])\n    [(0, 0), (1, 0), (1, 1), (0, 1)]\n\n    >>> graham_scan([(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)])\n    [(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)]\n\n    >>> graham_scan([(-100, 20), (99, 3), (1, 10000001), (5133186, -25), (-66, -4)])\n    [(5133186, -25), (1, 10000001), (-100, 20), (-66, -4)]\n    \"\"\"\n\n    if len(points) <= 2:\n        # There is no convex hull\n        raise ValueError(\"graham_scan: argument must contain more than 3 points.\")\n    if len(points) == 3:\n        return points\n    # find the lowest and the most left point\n    minidx = 0\n    miny, minx = maxsize, maxsize\n    for i, point in enumerate(points):\n        x = point[0]\n        y = point[1]\n        if y < miny:\n            miny = y\n            minx = x\n            minidx = i\n        if y == miny and x < minx:\n            minx = x\n            minidx = i\n\n    # remove the lowest and the most left point from points for preparing for sort\n    points.pop(minidx)","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/TheAlgorithms/Python/blob/f5988cc09713315817df6a7e327e258013a94440/other/graham_scan.py#L97-L133","documentation":"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.","triggerScenarios":"Calling graham_scan([(0,0)]), graham_scan([(0,0),(1,1)]), or graham_scan([]) — any list of 0, 1, or 2 (x, y) tuples.","commonSituations":"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.","solutions":["Check len(points) >= 3 before calling graham_scan and skip/short-circuit degenerate inputs","Filter or assert upstream so the point set never degenerates below 3 points","For exactly 3 points be aware the function returns them unsorted and without collinearity handling"],"exampleFix":"# before\nhull = graham_scan(points)  # ValueError when points has <= 2 entries\n\n# after\nhull = graham_scan(points) if len(points) >= 3 else list(points)","handlingStrategy":"validation","validationCode":"def has_hull(points) -> bool:\n    return len(points) >= 3","typeGuard":"from typing import Sequence\n\ndef is_scannable_point_set(points: Sequence[Sequence[float]]) -> bool:\n    \"\"\"True when graham_scan can process the input.\"\"\"\n    return len(points) >= 3 and all(len(p) >= 2 for p in points)","tryCatchPattern":"try:\n    hull = graham_scan(points)\nexcept ValueError as e:\n    if 'more than 3 points' in str(e):\n        hull = list(points)  # degenerate set: points are their own 'hull'\n    else:\n        raise","preventionTips":["Filter point clouds to at least 3 distinct points before running hull algorithms","Unit-test geometry pipelines with empty, single, and duplicate-point inputs"],"tags":["computational-geometry","convex-hull","input-validation"],"backgroundTag":null,"analyzedSha":"f5988cc09713315817df6a7e327e258013a94440","analyzedAt":"2026-08-14T17:30:07.041Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}