redis/redis-py · error · DataError

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

Error message

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

What it means

Raised by set() when more than one of the conditional switches (nx, xx, ifeq, ifne, ifdeq, ifdne) is supplied at the same time. These flags all control the SET-conditional behavior and Redis itself only allows one, so the client validates this up front via at_most_one_value_set() before building the command. It is a DataError (subclass of RedisError).

Solutions

  1. Inspect the set() call and keep only one conditional argument (nx, xx, ifeq, ifne, ifdeq, or ifdne).
  2. If you are building options dynamically, filter the conditional keys so at most one survives before calling set().
  3. For compare-and-swap use ifeq/ifne instead of combining nx/xx with a value comparison.

Example fix

# before
await client.set('k', 'v', nx=True, ifeq='expected')

# after
await client.set('k', 'v', ifeq='expected')
Defensive patterns

Strategy: validation

Validate before calling

CONDITIONAL_KWARGS = {'nx', 'xx', 'ifeq', 'ifne', 'ifdeq', 'ifdne'}
provided = {
    k: v for k, v in {
        'nx': nx, 'xx': xx,
        'ifeq': ifeq, 'ifne': ifne,
        'ifdeq': ifdeq, 'ifdne': ifdne,
    }.items()
    if (v is not None and v is not False)
}
if len(provided) > 1:
    raise ValueError(f'Only one conditional allowed, got: {list(provided)}')
await client.set('k', 'v', **provided)

Try / catch

from redis.exceptions import DataError
try:
    await client.set('k', 'v', nx=True, ifeq='x')
except DataError as e:
    if 'mutually exclusive' in str(e):
        # pick a single conditional and retry
        await client.set('k', 'v', ifeq='x')

Prevention

When it happens

Trigger: Calling client.set('k', 'v', nx=True, xx=True), or client.set('k', 'v', nx=True, ifeq='old'), or any combination where two or more of nx/xx/ifeq/ifne/ifdeq/ifdne are truthy or non-None simultaneously. Passing a list of conditional kwargs through **kwargs into set() can also trigger it unintentionally.

Common situations: Refactoring an NX-based lock to use IFEQ compare-and-swap but forgetting to remove nx=True. Building SET options dynamically from a dict and accidentally including two conditionals. Copy-pasting a set() call that already had xx=True and appending ifne for a new feature.

Related errors


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

Appendix: 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 6a6b581b48)