redis/redis-py · error · DataError

``nx``, ``xx``, ``ifeq``, ``ifne``, ``ifdeq``, ``ifdne`` are

Error message

``nx``, ``xx``, ``ifeq``, ``ifne``, ``ifdeq``, ``ifdne`` are mutually exclusive.

What it means

Raised by Redis.set() when more than one conditional-write switch is active. nx (set if not exists), xx (set if exists), ifeq (set if value matches digest), ifne (set if value differs), ifdeq, and ifdne are mutually exclusive because they all map to a single conditional clause in the Redis SET command. The client enforces this with at_most_one_value_set before issuing the command.

Source

Thrown at redis/commands/core.py:4427

            )
        ):
            raise DataError(
                "``ex``, ``px``, ``exat``, ``pxat``, "
                "and ``keepttl`` are mutually exclusive."
            )

        # Enforce mutual exclusivity among all conditional switches.
        if not at_most_one_value_set(
            (
                nx,
                xx,
                ifeq is not None,
                ifne is not None,
                ifdeq is not None,
                ifdne is not None,
            )
        ):
            raise DataError(
                "``nx``, ``xx``, ``ifeq``, ``ifne``, ``ifdeq``, ``ifdne`` are mutually exclusive."
            )

        pieces: list[EncodableT] = [name, value]
        options = {}

        # Conditional modifier (exactly one at most)
        if nx:
            pieces.append("NX")
        elif xx:
            pieces.append("XX")
        elif ifeq is not None:
            pieces.extend(("IFEQ", ifeq))
        elif ifne is not None:
            pieces.extend(("IFNE", ifne))
        elif ifdeq is not None:
            pieces.extend(("IFDEQ", ifdeq))
        elif ifdne is not None:

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Choose exactly one conditional modifier per set() call.
  2. Make nx/xx mutually exclusive in your caller logic (e.g. an enum or a single mode variable) rather than passing independent booleans.
  3. If you need compare-and-set semantics, use ifeq/ifne alone (requires Redis 8.4+) and do not combine with nx/xx.

Example fix

# before
await r.set("k", "v", nx=True, xx=True)
# after
await r.set("k", "v", nx=True)
Defensive patterns

Strategy: validation

Validate before calling

VALID_CONDITIONAL = {'nx', 'xx', 'ifeq', 'ifne', 'ifdeq', 'ifdne'}
def _at_most_one_conditional(nx, xx, ifeq, ifne, ifdeq, ifdne):
    active = [k for k, v in (('nx', nx), ('xx', xx), ('ifeq', ifeq), ('ifne', ifne), ('ifdeq', ifdeq), ('ifdne', ifdne)) if v not in (None, False)]
    if len(active) > 1:
        raise ValueError(f'Only one conditional allowed, got: {active}')
    return active[0] if active else None

Try / catch

from redis.exceptions import DataError
try:
    await r.set('k', 'v', nx=True)
except DataError as e:
    if 'mutually exclusive' in str(e):
        await r.set('k', 'v', nx=True)  # retry with single mode

Prevention

When it happens

Trigger: Calling client.set(key, val, nx=True, xx=True), or client.set(key, val, nx=True, ifeq=digest), i.e. enabling two or more of the nx/xx/ifeq/ifne/ifdeq/ifdne flags simultaneously.

Common situations: Code that toggles nx/xx conditionally but defaults both to a truthy value; merging two code paths where one sets nx and another sets xx; using ifeq/ifne (experimental_args, Redis 8.4+) alongside the older nx/xx flags.

Related errors


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