{"record":{"id":"704b55d379ec66c7","repo":"python/cpython","slug":"separator-should-contain-at-least-one-element","errorCode":null,"errorMessage":"Separator should contain at least one element","messagePattern":"Separator should contain at least one element","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/streams.py","lineNumber":609,"sourceCode":"        may contain the separator partially.\n\n        If the data cannot be read because of over limit, a\n        LimitOverrunError exception  will be raised, and the data\n        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.","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/streams.py#L591-L627","documentation":"ValueError raised by StreamReader.readuntil() when the separator argument is an empty tuple (after normalizing a single separator to a one-element list, the list is empty). At least one separator is required for the algorithm to know where a message ends, so the request is rejected before touching the buffer.","triggerScenarios":"await reader.readuntil(()) or readuntil(separators) where separators is an empty tuple/list-built value, e.g. built from user input or protocol config that ended up empty.","commonSituations":"A delimiter list parsed from a protocol/config string that produced no entries; code that filters delimiters (e.g. removing '\\r' conditionally) leaving an empty tuple; dynamically constructed separator sets in test fixtures.","solutions":["Validate the separator collection before calling readuntil and reject empty sets at the config boundary","Default to a sensible protocol delimiter (e.g. (b'\\r\\n', b'\\n')) when the dynamic set is empty","If EOF-terminated reading was intended, use reader.read(-1) instead of readuntil"],"exampleFix":"// before\nseps = tuple(s for s in configured if s.enabled)  # may be ()\nline = await reader.readuntil(seps)\n\n// after\nseps = tuple(s for s in configured if s.enabled) or (b'\\n',)\nline = await reader.readuntil(seps)","handlingStrategy":"validation","validationCode":"def normalize_separators(seps) -> tuple[bytes, ...]:\n    out = tuple(s for s in seps if s) if isinstance(seps, tuple) else (seps,)\n    if not out:\n        raise ValueError('readuntil requires at least one non-empty separator')\n    return out","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Build separator tuples from validated protocol tables, never raw user input","Unit-test framing config including the empty-collection edge case"],"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"}