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
- Choose exactly one conditional modifier per set() call.
- Make nx/xx mutually exclusive in your caller logic (e.g. an enum or a single mode variable) rather than passing independent booleans.
- 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
- Model the conditional mode as an enum and derive the boolean flags from it.
- Default nx/xx to False and only set the one you intend.
- Remember ifeq/ifne/ifdeq/ifdne are experimental and require Redis 8.4+.
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
- CLIENT KILL type must be one of {client_types!r}
- CLIENT KILL skipme must be a bool
- CLIENT KILL <filter> <value> ... ... <filter> <value> must s
- CLIENT LIST _type must be one of {client_types!r}
- client_id must be a list
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/4bbcce1d1f41adde.json.
Report an issue: GitHub.