redis/redis-py · error · DataError

XADD idmp must be a tuple of (producer_id, idempotent_id)

Error message

XADD idmp must be a tuple of (producer_id, idempotent_id)

What it means

Raised by xadd() when idmp is provided but is not a 2-element tuple. idmp must be a (producer_id, idempotent_id) pair where producer_id is a str and idempotent_id is bytes; the library destructures idmp[0] and idmp[1] when building the IDMP command clause.

Source

Thrown at redis/commands/core.py:7035

        if idmpauto is not None and idmp is not None:
            raise DataError("Only one of ```idmpauto``` or ```idmp``` may be specified")

        if (idmpauto is not None or idmp is not None) and id != "*":
            raise DataError("IDMPAUTO and IDMP can only be used with id='*'")

        if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
            raise DataError("XADD ref_policy must be one of: KEEPREF, DELREF, ACKED")

        if nomkstream:
            pieces.append(b"NOMKSTREAM")
        if ref_policy is not None:
            pieces.append(ref_policy)
        if idmpauto is not None:
            pieces.extend([b"IDMPAUTO", idmpauto])
        if idmp is not None:
            if not isinstance(idmp, tuple) or len(idmp) != 2:
                raise DataError(
                    "XADD idmp must be a tuple of (producer_id, idempotent_id)"
                )
            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)

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass idmp as a 2-tuple: idmp=('producer-1', b'idempotent-id').
  2. Ensure the second element is bytes (the idempotent ID).
  3. If you only have a producer ID, use idmpauto instead.

Example fix

# before
await r.xadd('s', {'f':'v'}, id='*', idmp='prod-1')
# after
await r.xadd('s', {'f':'v'}, id='*', idmp=('prod-1', b'abc123'))
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_idmp(idmp):
    if idmp is not None and (not isinstance(idmp, tuple) or len(idmp) != 2):
        raise ValueError('idmp must be a (producer_id, idempotent_id) tuple')
    return True

Type guard

def is_valid_idmp(idmp) -> bool:
    return idmp is None or (isinstance(idmp, tuple) and len(idmp) == 2)

Prevention

When it happens

Trigger: Calling client.xadd(name, fields, id='*', idmp='prod-1'), idmp=['p1', b'x'] (list not tuple), or idmp=('p1',) (single element).

Common situations: Passing a list instead of a tuple; passing only a producer ID; passing a flat string; misreading the type hint.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/518c0b8fc66acd8d.json. Report an issue: GitHub.