python/cpython · error · ValueError

Separator should contain at least one element

Error message

Separator should contain at least one element

What it means

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.

Source

Thrown at Lib/asyncio/streams.py:609

        may contain the separator partially.

        If the data cannot be read because of over limit, a
        LimitOverrunError exception  will be raised, and the data
        will be left in the internal buffer, so it can be read again.

        The ``separator`` may also be a tuple of separators. In this
        case the return value will be the shortest possible that has any
        separator as the suffix. For the purposes of LimitOverrunError,
        the shortest possible separator is considered to be the one that
        matched.
        """
        if isinstance(separator, tuple):
            # Makes sure shortest matches wins
            separator = sorted(separator, key=len)
        else:
            separator = [separator]
        if not separator:
            raise ValueError('Separator should contain at least one element')
        min_seplen = len(separator[0])
        max_seplen = len(separator[-1])
        if min_seplen == 0:
            raise ValueError('Separator should be at least one-byte string')

        if self._exception is not None:
            raise self._exception

        # Consume whole buffer except last bytes, which length is
        # one less than max_seplen. Let's check corner cases with
        # separator[-1]='SEPARATOR':
        # * we have received almost complete separator (without last
        #   byte). i.e buffer='some textSEPARATO'. In this case we
        #   can safely consume max_seplen - 1 bytes.
        # * last byte of buffer is first byte of separator, i.e.
        #   buffer='abcdefghijklmnopqrS'. We may safely consume
        #   everything except that last byte, but this require to
        #   analyze bytes of buffer that match partial separator.

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Validate the separator collection before calling readuntil and reject empty sets at the config boundary
  2. Default to a sensible protocol delimiter (e.g. (b'\r\n', b'\n')) when the dynamic set is empty
  3. If EOF-terminated reading was intended, use reader.read(-1) instead of readuntil

Example fix

// before
seps = tuple(s for s in configured if s.enabled)  # may be ()
line = await reader.readuntil(seps)

// after
seps = tuple(s for s in configured if s.enabled) or (b'\n',)
line = await reader.readuntil(seps)
Defensive patterns

Strategy: validation

Validate before calling

def normalize_separators(seps) -> tuple[bytes, ...]:
    out = tuple(s for s in seps if s) if isinstance(seps, tuple) else (seps,)
    if not out:
        raise ValueError('readuntil requires at least one non-empty separator')
    return out

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/704b55d379ec66c7. Report an issue: GitHub.