redis/redis-py · error · DataError
XADD ref_policy must be one of: KEEPREF, DELREF, ACKED
Error message
XADD ref_policy must be one of: KEEPREF, DELREF, ACKED
What it means
Raised by xadd() when ref_policy is not None and not one of KEEPREF, DELREF, or ACKED. ref_policy controls how consumer-group PEL references are handled during stream trimming on XADD; only those three tokens are valid. It is a DataError.
Solutions
- Pass ref_policy as exactly 'KEEPREF', 'DELREF', or 'ACKED' (uppercase), or leave it as None for default behavior.
- Validate/normalize the value against the allowed set before calling xadd.
Example fix
# before
client.xadd('s', {'f': 'v'}, maxlen=100, ref_policy='delref')
# after
client.xadd('s', {'f': 'v'}, maxlen=100, ref_policy='DELREF') Defensive patterns
Strategy: validation
Validate before calling
VALID_REF_POLICIES = {'KEEPREF', 'DELREF', 'ACKED'}
if ref_policy is not None and ref_policy not in VALID_REF_POLICIES:
raise ValueError(f'ref_policy must be one of {VALID_REF_POLICIES}')
client.xadd(name, fields, ref_policy=ref_policy) Type guard
from typing import Literal
def is_valid_ref_policy(p: str | None) -> bool:
return p is None or p in {'KEEPREF', 'DELREF', 'ACKED'}
# usage
ref_policy: Literal['KEEPREF', 'DELREF', 'ACKED'] | None = None Try / catch
from redis.exceptions import DataError
try:
client.xadd(name, fields, ref_policy=ref_policy)
except DataError as e:
if 'ref_policy' in str(e):
client.xadd(name, fields, ref_policy=None) Prevention
- Use Literal['KEEPREF','DELREF','ACKED'] | None as the type annotation for ref_policy.
- Validate/normalize external config values before passing them to xadd.
When it happens
Trigger: Calling client.xadd('mystream', fields, ref_policy='keep') (wrong case), ref_policy='DELETE', or any string outside the allowed set.
Common situations: Lowercase/typo in the policy name, or forwarding an unvalidated user/config value.
Related errors
- XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED
- IDMPAUTO and IDMP can only be used with id='*'
- Only one of ```idmpauto``` or ```idmp``` may be specified
- Only one of ```maxlen``` or ```minid``` may be specified
- XADD fields must be a non-empty dict
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/7a81acfda2ec1a9c.
Report an issue: GitHub.
Appendix: source
Thrown at redis/commands/core.py:7025
with id='*'. The producer ID must be unique per producer and consistent across
restarts. The idempotent ID must be unique per message and per producer.
Shorter idempotent IDs require less memory and allow faster processing.
Creates an IDMP map if it doesn't exist yet.
For more information, see https://redis.io/commands/xadd
"""
pieces: list[EncodableT] = []
if maxlen is not None and minid is not None:
raise DataError("Only one of ```maxlen``` or ```minid``` may be specified")
if idmpauto is not None and idmp is not None:
raise DataError("Only one of ```idmpauto``` or ```idmp``` may be specified")
if (idmpauto is not None or idmp is not None) and id != "*":
raise DataError("IDMPAUTO and IDMP can only be used with id='*'")
if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
raise DataError("XADD ref_policy must be one of: KEEPREF, DELREF, ACKED")
if nomkstream:
pieces.append(b"NOMKSTREAM")
if ref_policy is not None:
pieces.append(ref_policy)
if idmpauto is not None:
pieces.extend([b"IDMPAUTO", idmpauto])
if idmp is not None:
if not isinstance(idmp, tuple) or len(idmp) != 2:
raise DataError(
"XADD idmp must be a tuple of (producer_id, idempotent_id)"
)
pieces.extend([b"IDMP", idmp[0], idmp[1]])
if maxlen is not None:
if not isinstance(maxlen, int) or maxlen < 0:
raise DataError("XADD maxlen must be non-negative integer")
pieces.append(b"MAXLEN")
if approximate:View on GitHub (pinned to 6a6b581b48)