redis/redis-py · error · DataError
XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED
Error message
XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED
What it means
Raised by Redis.xdelex() when ref_policy is not one of 'KEEPREF', 'DELREF', 'ACKED'. The check is a set membership test; values are case-sensitive and must be uppercase.
Solutions
- Use one of the exact uppercase literals: 'KEEPREF', 'DELREF', or 'ACKED'.
- Coerce config: ref_policy = str(ref_policy).upper(); validate against the set.
Example fix
# before
r.xdelex('mystream', '1-0', ref_policy='keepref')
# after
r.xdelex('mystream', '1-0', ref_policy='KEEPREF') Defensive patterns
Strategy: validation
Validate before calling
POLICIES = {'KEEPREF', 'DELREF', 'ACKED'}
ref_policy = str(ref_policy).upper()
if ref_policy not in POLICIES:
raise ValueError('ref_policy must be one of %s' % POLICIES) Type guard
lambda p: p in {'KEEPREF', 'DELREF', 'ACKED'} Try / catch
from redis.exceptions import DataError
try:
r.xdelex('mystream', *ids, ref_policy=rp)
except DataError as e:
log.warning('bad xdelex ref_policy %r: %s', rp, e) Prevention
- Uppercase and validate enum values at the config boundary; the check is case-sensitive.
When it happens
Trigger: Calling r.xdelex('mystream', '1-0', ref_policy='keepref') (lowercase), ref_policy='DELETE', or a typo like 'KEEPRF'.
Common situations: Lowercasing the value from config, guessing the enum, or mixing it up with XNACK modes.
Related errors
- XDELEX requires at least one message ID
- XNACK mode must be one of: SILENT, FAIL, FATAL
- CLIENT KILL type must be one of
- if XPENDING is provided with idle time or consumername, it…
- XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/5ba1719a36c9eb88.
Report an issue: GitHub.
Appendix: source
Thrown at redis/commands/core.py:7378
*ids: StreamIdT,
ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
) -> Awaitable[int]: ...
def xdelex(
self,
name: KeyT,
*ids: StreamIdT,
ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
) -> int | Awaitable[int]:
"""
Extended version of XDEL that provides more control over how message entries
are deleted concerning consumer groups.
"""
if not ids:
raise DataError("XDELEX requires at least one message ID")
if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
raise DataError("XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED")
pieces = [name, ref_policy, "IDS", len(ids)]
pieces.extend(ids)
return self.execute_command("XDELEX", *pieces)
@overload
def xgroup_create(
self: SyncClientProtocol,
name: KeyT,
groupname: GroupT,
id: StreamIdT = "$",
mkstream: bool = False,
entries_read: int | None = None,
) -> bool: ...
@overload
def xgroup_create(
self: AsyncClientProtocol,View on GitHub (pinned to 6a6b581b48)