redis/redis-py · error · DataError

XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED

Error message

XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED

What it means

Raised by xackdel() when ref_policy is not one of KEEPREF, DELREF, or ACKED. The reference policy controls how consumer-group pending entries are treated when acknowledging-and-deleting, and Redis only accepts those three literal tokens. It is a DataError.

Solutions

  1. Use exactly one of 'KEEPREF', 'DELREF', or 'ACKED' (uppercase) for ref_policy.
  2. If the value comes from user input, normalize to uppercase and validate against the allowed set before calling.

Example fix

# before
client.xackdel('mystream', 'mygroup', '1234-0', ref_policy='keepref')

# after
client.xackdel('mystream', 'mygroup', '1234-0', ref_policy='KEEPREF')
Defensive patterns

Strategy: validation

Validate before calling

VALID_REF_POLICIES = {'KEEPREF', 'DELREF', 'ACKED'}
if ref_policy not in VALID_REF_POLICIES:
    raise ValueError(f'ref_policy must be one of {VALID_REF_POLICIES}')
client.xackdel(name, group, *ids, ref_policy=ref_policy)

Type guard

from typing import Literal

def is_valid_ref_policy(p: str) -> bool:
    return p in {'KEEPREF', 'DELREF', 'ACKED'}

# usage
ref_policy: Literal['KEEPREF', 'DELREF', 'ACKED'] = 'KEEPREF'

Try / catch

from redis.exceptions import DataError
try:
    client.xackdel(name, group, *ids, ref_policy=ref_policy)
except DataError as e:
    if 'ref_policy' in str(e):
        client.xackdel(name, group, *ids, ref_policy='KEEPREF')

Prevention

When it happens

Trigger: Calling client.xackdel('mystream', 'mygroup', '1234-0', ref_policy='keep') (wrong case), ref_policy='REMOVE', or any value outside the allowed set.

Common situations: Using lowercase or a typo for the policy name, or passing a user-provided string without validation.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/61de02f39a8129ea. Report an issue: GitHub.

Appendix: source

Thrown at redis/commands/core.py:6932

    ) -> Awaitable[int]: ...

    def xackdel(
        self,
        name: KeyT,
        groupname: GroupT,
        *ids: StreamIdT,
        ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
    ) -> int | Awaitable[int]:
        """
        Combines the functionality of XACK and XDEL. Acknowledges the specified
        message IDs in the given consumer group and simultaneously attempts to
        delete the corresponding entries from the stream.
        """
        if not ids:
            raise DataError("XACKDEL requires at least one message ID")

        if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
            raise DataError("XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED")

        pieces = [name, groupname, ref_policy, "IDS", len(ids)]
        pieces.extend(ids)
        return self.execute_command("XACKDEL", *pieces)

    @overload
    def xadd(
        self: SyncClientProtocol,
        name: KeyT,
        fields: Dict[FieldT, EncodableT],
        id: StreamIdT = "*",
        maxlen: int | None = None,
        approximate: bool = True,
        nomkstream: bool = False,
        minid: StreamIdT | None = None,
        limit: int | None = None,
        ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] | None = None,
        idmpauto: str | None = None,

View on GitHub (pinned to 6a6b581b48)