{"record":{"id":"b6abe73090214557","repo":"redis/redis-py","slug":"method-discard-is-not-supported-outside-of-trans-b6abe7","errorCode":null,"errorMessage":"method discard() is not supported outside of transactional context","messagePattern":"method discard\\(\\) is not supported outside of transactional context","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/cluster.py","lineNumber":4552,"sourceCode":"        elif request_policy == RequestPolicy.MULTI_SHARD:\n            nodes = policy_callback(*args, **kwargs)\n        elif request_policy == RequestPolicy.DEFAULT_KEYLESS:\n            nodes = policy_callback(args[0])\n        else:\n            nodes = policy_callback()\n\n        if args[0].lower() == \"ft.aggregate\":\n            self._aggregate_nodes = nodes\n\n        return nodes\n\n    def multi(self):\n        raise RedisClusterException(\n            \"method multi() is not supported outside of transactional context\"\n        )\n\n    def discard(self):\n        raise RedisClusterException(\n            \"method discard() is not supported outside of transactional context\"\n        )\n\n    def watch(self, *names):\n        raise RedisClusterException(\n            \"method watch() is not supported outside of transactional context\"\n        )\n\n    def unwatch(self, *names):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    def delete(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"deleting multiple keys is not implemented in pipeline command\"\n            )","sourceCodeStart":4534,"sourceCodeEnd":4570,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/cluster.py#L4534-L4570","documentation":"Raised by AbstractStrategy.discard() in redis/cluster.py:4552. DISCARD aborts a MULTI transaction, but a non-transactional cluster pipeline never entered MULTI, so there is nothing to discard. The cluster pipeline exposes discard only on its transactional strategy path.","triggerScenarios":"Calling pipe.discard() on a non-transactional ClusterPipeline (rc.pipeline(transaction=False) or default), or calling discard before multi/transaction was started.","commonSituations":"Standalone pipeline code ported to cluster; exception handlers that unconditionally call pipe.discard() to clean up.","solutions":["Only call discard() inside a transactional pipeline (rc.pipeline(transaction=True)) after MULTI was implicitly started.","For a non-transactional pipeline, call pipe.reset() to clear the command queue instead of discard().","Guard the discard call so it only runs when a transaction is actually in progress."],"exampleFix":"// before\npipe = rc.pipeline()\ntry:\n    pipe.set('k', 'v')\n    pipe.discard()\n// after\npipe = rc.pipeline()\npipe.reset()  # clear non-transactional queue","handlingStrategy":"validation","validationCode":"if getattr(pipe, 'transaction', False):\n    pipe.discard()\nelse:\n    pipe.reset()","typeGuard":"def is_transactional_pipe(pipe) -> bool:\n    return bool(getattr(pipe, 'transaction', False))","tryCatchPattern":"from redis.exceptions import RedisClusterException\ntry:\n    pipe.discard()\nexcept RedisClusterException:\n    pipe.reset()","preventionTips":["Use reset() on non-transactional pipelines.","Guard cleanup calls behind a transaction check."],"tags":["cluster","pipeline","transaction","api-misuse"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}