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
- Inspect the set() call and keep only one conditional argument (nx, xx, ifeq, ifne, ifdeq, or ifdne).
- If you are building options dynamically, filter the conditional keys so at most one survives before calling set().
- 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
- Keep SET conditional flags in a single variable and pass it through, never hardcode two.
- When refactoring from nx/xx to ifeq/ifne, grep for the old flag and remove it.
- Build the conditional kwargs dict dynamically and assert len <= 1 before calling set().
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
- ex must be datetime.timedelta or int
- len and idx cannot be provided together.
- Only one of ```idmpauto``` or ```idmp``` may be specified
- Only one of ```maxlen``` or ```minid``` may be specified
- px must be datetime.timedelta or int
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)