redis/redis-py · error · ValueError

Only one of IFEQ/IFNE/IFDEQ/IFDNE may be specified

Error message

Only one of IFEQ/IFNE/IFDEQ/IFDNE may be specified

What it means

delex() (experimental, Redis 8.4+) accepts at most one conditional modifier among IFEQ, IFNE, IFDEQ, IFDNE. Passing two or more is contradictory (you cannot require both equal-and-not-equal) and is rejected at core.py:2931 with ValueError. Note this raises ValueError, not DataError.

Source

Thrown at redis/commands/core.py:2932

            ifeq match-valu: Optional[Union[bytes, str]] - Delete the key only if its value is equal to match-value
            ifne match-value: Optional[Union[bytes, str]] - Delete the key only if its value is not equal to match-value
            ifdeq match-digest: Optional[str] - Delete the key only if the digest of its value is equal to match-digest
            ifdne match-digest: Optional[str] - Delete the key only if the digest of its value is not equal to match-digest

        Returns:
            int: 1 if the key was deleted, 0 otherwise.
        Raises:
            redis.exceptions.ResponseError: if key exists but is not a string
                                            and a condition is specified.
            ValueError: if more than one condition is provided.


        Requires Redis 8.4 or greater.
        For more information, see https://redis.io/commands/delex
        """
        conds = [x is not None for x in (ifeq, ifne, ifdeq, ifdne)]
        if sum(conds) > 1:
            raise ValueError("Only one of IFEQ/IFNE/IFDEQ/IFDNE may be specified")

        pieces = ["DELEX", name]
        if ifeq is not None:
            pieces += ["IFEQ", ifeq]
        elif ifne is not None:
            pieces += ["IFNE", ifne]
        elif ifdeq is not None:
            pieces += ["IFDEQ", ifdeq]
        elif ifdne is not None:
            pieces += ["IFDNE", ifdne]

        return self.execute_command(*pieces)

    @overload
    def dump(self: SyncClientProtocol, name: KeyT) -> bytes | None: ...

    @overload
    def dump(self: AsyncClientProtocol, name: KeyT) -> Awaitable[bytes | None]: ...

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass exactly one condition keyword, e.g. r.delex('key', ifeq='expected').
  2. If you need multi-condition logic, issue separate delex calls or implement client-side comparison after a GET.
  3. Catch ValueError specifically (not DataError) when wrapping delex in error handling.

Example fix

# before
r.delex('key', ifeq='a', ifne='b')

# after
r.delex('key', ifeq='a')  # pick the single condition you actually need
Defensive patterns

Strategy: validation

Validate before calling

conds = [ifeq, ifne, ifdeq, ifdne]
if sum(c is not None for c in conds) > 1:
    raise ValueError('delex: pass at most one of ifeq/ifne/ifdeq/ifdne')
r.delex('key', ifeq=ifeq, ifne=ifne, ifdeq=ifdeq, ifdne=ifdne)

Type guard

def valid_delex_conditions(ifeq, ifne, ifdeq, ifdne) -> bool:
    return sum(x is not None for x in (ifeq, ifne, ifdeq, ifdne)) <= 1

Try / catch

try:
    r.delex('key', ifeq=ifeq, ifne=ifne, ifdeq=ifdeq, ifdne=ifdne)
except ValueError as e:
    if 'Only one of IFEQ' in str(e):
        # pick the highest-priority condition and retry
        r.delex('key', ifeq=ifeq)
    else:
        raise

Prevention

When it happens

Trigger: r.delex('key', ifeq='a', ifne='b'), or r.delex('key', ifdeq=digest, ifdne=other). Any pair of the four condition kwargs triggers sum(conds) > 1.

Common situations: Building a generic conditional-delete helper that forwards multiple optional filters; misunderstanding the semantics and assuming conditions are AND-combined.

Related errors


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