redis/redis-py · error · DataError

XADD fields must be a non-empty dict

Error message

XADD fields must be a non-empty dict

What it means

Raised by xadd() when fields is not a dict or is an empty dict. XADD requires at least one field/value pair in the stream entry; passing a list, None, or {} is invalid. It is a DataError.

Solutions

  1. Pass a non-empty dict of field/value pairs, e.g. {'field1': 'value1'}.
  2. If the dict may be empty at runtime, guard with 'if fields:' before calling xadd.

Example fix

# before
client.xadd('mystream', {})

# after
client.xadd('mystream', {'field1': 'value1'})
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(fields, dict) or not fields:
    raise ValueError('fields must be a non-empty dict')
client.xadd(name, fields)

Type guard

def is_valid_fields(v) -> bool:
    return isinstance(v, dict) and len(v) > 0

# usage
if not is_valid_fields(fields):
    raise TypeError('fields must be a non-empty dict')

Try / catch

from redis.exceptions import DataError
try:
    client.xadd(name, fields)
except DataError as e:
    if 'fields must be a non-empty dict' in str(e):
        fields = {'placeholder': '1'}
        client.xadd(name, fields)

Prevention

When it happens

Trigger: Calling client.xadd('mystream', {}), client.xadd('mystream', [('f', 'v')]) (list of pairs), or client.xadd('mystream', None).

Common situations: Building fields from a query/result that produced no rows, or passing a list of tuples instead of a dict.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/ff66b2e2c8463176. Report an issue: GitHub.

Appendix: source

Thrown at redis/commands/core.py:7055

                )
            pieces.extend([b"IDMP", idmp[0], idmp[1]])
        if maxlen is not None:
            if not isinstance(maxlen, int) or maxlen < 0:
                raise DataError("XADD maxlen must be non-negative integer")
            pieces.append(b"MAXLEN")
            if approximate:
                pieces.append(b"~")
            pieces.append(str(maxlen))
        if minid is not None:
            pieces.append(b"MINID")
            if approximate:
                pieces.append(b"~")
            pieces.append(minid)
        if limit is not None:
            pieces.extend([b"LIMIT", limit])
        pieces.append(id)
        if not isinstance(fields, dict) or len(fields) == 0:
            raise DataError("XADD fields must be a non-empty dict")
        for pair in fields.items():
            pieces.extend(pair)
        return self.execute_command("XADD", name, *pieces)

    @overload
    def xcfgset(
        self: SyncClientProtocol,
        name: KeyT,
        idmp_duration: int | None = None,
        idmp_maxsize: int | None = None,
    ) -> bytes | str: ...

    @overload
    def xcfgset(
        self: AsyncClientProtocol,
        name: KeyT,
        idmp_duration: int | None = None,
        idmp_maxsize: int | None = None,

View on GitHub (pinned to 6a6b581b48)