redis/redis-py · error · DataError
``ex``, ``px``, ``exat``, ``pxat``, and ``keepttl`` are…
Error message
``ex``, ``px``, ``exat``, ``pxat``, and ``keepttl`` are mutually exclusive.
What it means
Raised as a `DataError` by `msetex()` (redis/commands/core.py:3875) via `at_most_one_value_set(...)` when more than one of `ex`, `px`, `exat`, `pxat`, or `keepttl` is set. MSETEX (Redis 8.4+) applies one TTL policy across the whole key/value mapping; mixing them is rejected client-side.
Solutions
- Set exactly one of `ex`, `px`, `exat`, `pxat`, or `keepttl=True`.
- Resolve a single TTL policy before calling and forward only that kwarg.
- Validate with the same `at_most_one_value_set` helper if you wrap MSETEX.
Example fix
// before
r.msetex({'a': 1, 'b': 2}, ex=10, keepttl=True)
// after
r.msetex({'a': 1, 'b': 2}, ex=10) # 10s TTL on both keys Defensive patterns
Strategy: validation
Validate before calling
expiry = [a for a in (ex, px, exat, pxat) if a is not None]
if len(expiry) + (1 if keepttl else 0) > 1:
raise ValueError('msetex: at most one of ex/px/exat/pxat/keepttl')
r.msetex(mapping, **({} if not expiry else {'ex': expiry[0]}), keepttl=keepttl) Prevention
- Forward a single resolved TTL to MSETEX.
- Never combine keepttl with a new expiry in shared helpers.
When it happens
Trigger: `r.msetex({'a': 1, 'b': 2}, ex=10, keepttl=True)`, or any MSETEX call combining two or more expiry kwargs.
Common situations: Shared expiry helpers reused across SET/MSETEX; config objects carrying both a TTL and a keepttl flag; refactoring that leaves stale kwargs in place.
Related errors
- ``enx`` requires one of ``ex``, ``px``, ``exat``, or…
- ``ex``, ``px``, ``exat``, ``pxat``, and ``persist`` are…
- bit must be 0 or 1
- Both start and end must be specified
- ``byfloat`` and ``byint`` are mutually exclusive.
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/c7117076d08a332a.
Report an issue: GitHub.
Appendix: source
Thrown at redis/commands/core.py:3884
specified in unix time.
``keepttl`` if True, retain the time to live associated with the keys.
Returns the number of fields that were added.
Available since Redis 8.4
For more information, see https://redis.io/commands/msetex
"""
if not at_most_one_value_set(
(
ex is not None,
px is not None,
exat is not None,
pxat is not None,
keepttl,
)
):
raise DataError(
"``ex``, ``px``, ``exat``, ``pxat``, "
"and ``keepttl`` are mutually exclusive."
)
exp_options: list[EncodableT] = []
if data_persist_option:
exp_options.append(data_persist_option.value)
exp_options.extend(extract_expire_flags(ex, px, exat, pxat))
if keepttl:
exp_options.append("KEEPTTL")
pieces = ["MSETEX", len(mapping)]
for pair in mapping.items():
pieces.extend(pair)
View on GitHub (pinned to 6a6b581b48)