{"record":{"id":"f48ee5dc10978952","repo":"python/cpython","slug":"invalid-number-of-bytes-to-read","errorCode":null,"errorMessage":"invalid number of bytes to read","messagePattern":"invalid number of bytes to read","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1085,"sourceCode":"        self._read_lock = Lock()\n\n    def readable(self):\n        return self.raw.readable()\n\n    def _reset_read_buf(self):\n        self._read_buf = b\"\"\n        self._read_pos = 0\n\n    def read(self, size=None):\n        \"\"\"Read size bytes.\n\n        Returns exactly size bytes of data unless the underlying raw IO\n        stream reaches EOF or if the call would block in non-blocking\n        mode. If size is negative, read until EOF or until read() would\n        block.\n        \"\"\"\n        if size is not None and size < -1:\n            raise ValueError(\"invalid number of bytes to read\")\n        with self._read_lock:\n            return self._read_unlocked(size)\n\n    def _read_unlocked(self, n=None):\n        nodata_val = b\"\"\n        empty_values = (b\"\", None)\n        buf = self._read_buf\n        pos = self._read_pos\n\n        # Special case for when the number of bytes to read is unspecified.\n        if n is None or n == -1:\n            self._reset_read_buf()\n            if hasattr(self.raw, 'readall'):\n                chunk = self.raw.readall()\n                if chunk is None:\n                    return buf[pos:] or None\n                else:\n                    return buf[pos:] + chunk","sourceCodeStart":1067,"sourceCodeEnd":1103,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1067-L1103","documentation":"Raised by BufferedReader.read(size) when an explicit size is less than -1. In the io API, size=None and size=-1 both mean 'read to EOF'; any other negative value has no defined meaning and is rejected with ValueError before the read lock is taken.","triggerScenarios":"reader.read(-2), or read(n) where n is a negative sentinel from caller code (e.g. -1 used for 'unlimited' colliding with another -1-means-something-else convention, decremented past -1).","commonSituations":"Passing a 'remaining bytes' counter that has gone negative (reading past an expected length); forwarding a size from an API where negatives have different semantics; arithmetic like size - already_read producing < -1.","solutions":["Use read() or read(-1) for read-to-EOF; use non-negative sizes otherwise.","Clamp before calling: reader.read(max(0, remaining)) — and skip the call when remaining == 0.","Fix underflow in length arithmetic (loop condition should stop before remaining goes negative)."],"exampleFix":"# before\nwhile remaining:\n    chunk = reader.read(remaining)  # remaining can hit -2 -> ValueError\n\n# after\nwhile remaining > 0:\n    chunk = reader.read(remaining)\n    ...\n    remaining -= len(chunk)","handlingStrategy":"validation","validationCode":"def safe_read(reader, n):\n    if n is None or n == -1:\n        return reader.read()\n    if n < 0:\n        raise ValueError(f\"bad read size: {n}\")\n    return reader.read(n)","typeGuard":null,"tryCatchPattern":"try:\n    chunk = reader.read(n)\nexcept ValueError as e:\n    if \"invalid number of bytes\" in str(e):\n        chunk = reader.read(max(0, n))\n    else:\n        raise","preventionTips":["Loop with 'while remaining > 0', not 'while remaining'.","Map 'unlimited' sentinels to None/-1 exactly once at the boundary.","Never forward external sizes unchecked into read()."],"tags":["python","io","buffered-reader","valueerror","read","validation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}