{"id":"5676bf36fbc0698f","repo":"redis/redis-py","slug":"ex-px-exat-pxat-and-persist","errorCode":null,"errorMessage":"``ex``, ``px``, ``exat``, ``pxat``, and ``persist`` are mutually exclusive.","messagePattern":"``ex``, ``px``, ``exat``, ``pxat``, and ``persist`` are mutually exclusive\\.","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":3283,"sourceCode":"        specified in unix time.\n\n        ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,\n        specified in unix time.\n\n        ``persist`` remove the time to live associated with ``name``.\n\n        For more information, see https://redis.io/commands/getex\n        \"\"\"\n        if not at_most_one_value_set(\n            (\n                ex is not None,\n                px is not None,\n                exat is not None,\n                pxat is not None,\n                persist,\n            )\n        ):\n            raise DataError(\n                \"``ex``, ``px``, ``exat``, ``pxat``, \"\n                \"and ``persist`` are mutually exclusive.\"\n            )\n\n        exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)\n\n        if persist:\n            exp_options.append(\"PERSIST\")\n\n        return self.execute_command(\"GETEX\", name, *exp_options)\n\n    def __getitem__(self, name: KeyT):\n        \"\"\"\n        Return the value at key ``name``, raises a KeyError if the key\n        doesn't exist.\n        \"\"\"\n        value = self.get(name)\n        if value is not None:","sourceCodeStart":3265,"sourceCodeEnd":3301,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L3265-L3301","documentation":"getex() sets or removes a key's TTL via exactly one mechanism. ex (seconds), px (ms), exat (unix-seconds), pxat (unix-ms), and persist are mutually exclusive; at most one may be set. The at_most_one_value_set check at core.py:3274 enforces this with DataError.","triggerScenarios":"r.getex('key', ex=10, px=1000), r.getex('key', exat=..., persist=True), or any combination of two or more of the five options.","commonSituations":"Generic TTL helper that forwards multiple optional kwargs; config-driven expiry where seconds and ms were both populated; refactoring from set() which uses keepttl instead of persist.","solutions":["Choose one expiry unit and pass only it, e.g. r.getex('key', ex=60).","Use persist=True alone to strip the TTL, or an expiry option alone to set it.","Centralize expiry selection in a helper that returns exactly one of the five."],"exampleFix":"# before\nr.getex('key', ex=60, px=60000)\n\n# after\nr.getex('key', ex=60)  # pick one unit","handlingStrategy":"validation","validationCode":"expiry = {'ex': ex, 'px': px, 'exat': exat, 'pxat': pxat}\nif persist:\n    expiry['persist'] = True\nset_count = sum(v is not None and v is not False for v in expiry.values())\nif set_count > 1:\n    raise ValueError('getex: pass exactly one expiry option')\nr.getex('key', **expiry)","typeGuard":"def valid_single_expiry(ex, px, exat, pxat, persist) -> bool:\n    return sum(bool(x) for x in (ex, px, exat, pxat, persist)) <= 1","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.getex('key', ex=ex, px=px)\nexcept DataError as e:\n    if 'mutually exclusive' in str(e):\n        r.getex('key', ex=ex)  # keep one, drop the rest\n    else:\n        raise","preventionTips":["Resolve to a single expiry kwarg in a helper before calling getex.","Treat persist as an alternative to expiry, never combined."],"tags":["redis","getex","validation","parameter-conflict","expiry"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}