{"record":{"id":"97e2e4083477b187","repo":"tursodatabase/turso","slug":"size-must-be-non-negative","errorCode":null,"errorMessage":"size must be non-negative","messagePattern":"size must be non-negative","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"bindings/python/turso/lib.py","lineNumber":1103,"sourceCode":"            except TypeError:\n                _reject_stdlib_row_factory(rf)\n                raise\n        # Fallback: return tuple\n        return row_values\n\n    def fetchone(self) -> Any:\n        self._ensure_open()\n        row = self._fetchone_tuple()\n        if row is None:\n            return None\n        return self._apply_row_factory(row)\n\n    def fetchmany(self, size: Optional[int] = None) -> list[Any]:\n        self._ensure_open()\n        if size is None:\n            size = self.arraysize\n        if size < 0:\n            raise ValueError(\"size must be non-negative\")\n        result: list[Any] = []\n        for _ in range(size):\n            row = self._fetchone_tuple()\n            if row is None:\n                break\n            result.append(self._apply_row_factory(row))\n        return result\n\n    def fetchall(self) -> list[Any]:\n        self._ensure_open()\n        result: list[Any] = []\n        while True:\n            row = self._fetchone_tuple()\n            if row is None:\n                break\n            result.append(self._apply_row_factory(row))\n        return result\n","sourceCodeStart":1085,"sourceCodeEnd":1121,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/python/turso/lib.py#L1085-L1121","documentation":"Cursor.fetchmany(size) raises ValueError when size is negative. When size is None it falls back to cursor.arraysize, which matches the stdlib sqlite3 behavior. The check exists only for negative numbers; zero is legal and returns an empty list.","triggerScenarios":"Calling cur.fetchmany(-1), often intending 'give me everything' as some other APIs allow; a size computed from pagination arithmetic such as limit - already_fetched that underflows to negative.","commonSituations":"Developers coming from APIs where -1 means 'all rows'; pagination code whose remaining-count math goes negative at the last page.","solutions":["Call fetchall() when you want every remaining row","Clamp the computed size: cur.fetchmany(max(0, size))","Fix the pagination arithmetic so remaining rows never go negative"],"exampleFix":"# before\nrows = cur.fetchmany(limit - fetched_so_far)  # -5 on the last page\n\n# after\nrows = cur.fetchmany(max(0, limit - fetched_so_far))","handlingStrategy":"validation","validationCode":"def fetchmany_safe(cur, size):\n    if size is None:\n        return cur.fetchmany()\n    if size < 0:\n        raise ValueError(\"size must be >= 0; use fetchall() for all rows\")\n    return cur.fetchmany(size)","typeGuard":null,"tryCatchPattern":"try:\n    rows = cur.fetchmany(size)\nexcept ValueError:\n    rows = cur.fetchall()  # negative size usually means 'everything' was intended","preventionTips":["Use fetchall() when you want all remaining rows","Clamp computed page sizes with max(0, n)","Do not port the '-1 means all' convention from other client libraries"],"tags":["python","db-api","fetchmany","argument-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}