{"record":{"id":"fde9faf9f2e3e4a8","repo":"python/cpython","slug":"separator-should-be-at-least-one-byte-string","errorCode":null,"errorMessage":"Separator should be at least one-byte string","messagePattern":"Separator should be at least one-byte string","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/streams.py","lineNumber":613,"sourceCode":"        will be left in the internal buffer, so it can be read again.\n\n        The ``separator`` may also be a tuple of separators. In this\n        case the return value will be the shortest possible that has any\n        separator as the suffix. For the purposes of LimitOverrunError,\n        the shortest possible separator is considered to be the one that\n        matched.\n        \"\"\"\n        if isinstance(separator, tuple):\n            # Makes sure shortest matches wins\n            separator = sorted(separator, key=len)\n        else:\n            separator = [separator]\n        if not separator:\n            raise ValueError('Separator should contain at least one element')\n        min_seplen = len(separator[0])\n        max_seplen = len(separator[-1])\n        if min_seplen == 0:\n            raise ValueError('Separator should be at least one-byte string')\n\n        if self._exception is not None:\n            raise self._exception\n\n        # Consume whole buffer except last bytes, which length is\n        # one less than max_seplen. Let's check corner cases with\n        # separator[-1]='SEPARATOR':\n        # * we have received almost complete separator (without last\n        #   byte). i.e buffer='some textSEPARATO'. In this case we\n        #   can safely consume max_seplen - 1 bytes.\n        # * last byte of buffer is first byte of separator, i.e.\n        #   buffer='abcdefghijklmnopqrS'. We may safely consume\n        #   everything except that last byte, but this require to\n        #   analyze bytes of buffer that match partial separator.\n        #   This is slow and/or require FSM. For this case our\n        #   implementation is not optimal, since require rescanning\n        #   of data that is known to not belong to separator. In\n        #   real world, separator will not be so long to notice","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/streams.py#L595-L631","documentation":"ValueError raised by StreamReader.readuntil() when the shortest separator is a zero-length bytes object (min_seplen == 0). A zero-length separator would match everywhere and make the consume-buffer arithmetic (which reserves max_seplen - 1 bytes) degenerate, so it is rejected upfront.","triggerScenarios":"await reader.readuntil(b''), or readuntil((b'', b'\\n')) where sorting by length puts the empty separator first; also a tuple containing an empty element after dynamic construction.","commonSituations":"Splitting user-supplied input on a delimiter where the user passed an empty string; separator lists assembled from parsed headers (e.g. an empty value for one terminator); copy-paste of str.split-style habits where '' is tolerated.","solutions":["Filter empty separators out of the collection before calling readuntil","Validate delimiter strings at the API/config boundary: must be non-empty bytes","If you intended an any-position single-byte match, iterate bytes manually or use read() and buffer splitting yourself"],"exampleFix":"// before\nseps = tuple(d.encode() for d in delims)  # may contain b''\nline = await reader.readuntil(seps)\n\n// after\nseps = tuple(d.encode() for d in delims if d)\nif not seps:\n    raise ValueError('at least one non-empty delimiter required')\nline = await reader.readuntil(seps)","handlingStrategy":"validation","validationCode":"def checked_separator(sep: bytes) -> bytes:\n    if not isinstance(sep, (bytes, bytearray)) or len(sep) == 0:\n        raise ValueError(f'separator must be non-empty bytes, got {sep!r}')\n    return bytes(sep)","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Reject empty delimiters wherever delimiters enter the system (CLI, config, protocol negotiation)","Remember readuntil accepts bytes, not str — encode first"],"tags":["asyncio","streams","validation","readuntil"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}