{"id":"3efcc5de19a87184","repo":"redis/redis-py","slug":"method-discard-is-not-supported-outside-of-trans","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/asyncio/cluster.py","lineNumber":3047,"sourceCode":"        self._command_queue = []\n\n    def multi(self):\n        raise RedisClusterException(\n            \"method multi() is not supported outside of transactional context\"\n        )\n\n    async def watch(self, *names):\n        raise RedisClusterException(\n            \"method watch() is not supported outside of transactional context\"\n        )\n\n    async def unwatch(self):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    async def discard(self):\n        raise RedisClusterException(\n            \"method discard() is not supported outside of transactional context\"\n        )\n\n    async def unlink(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"unlinking multiple keys is not implemented in pipeline command\"\n            )\n\n        return self.execute_command(\"UNLINK\", names[0])\n\n\nclass TransactionStrategy(AbstractStrategy):\n    NO_SLOTS_COMMANDS = {\"UNWATCH\"}\n    IMMEDIATE_EXECUTE_COMMANDS = {\"WATCH\", \"UNWATCH\"}\n    UNWATCH_COMMANDS = {\"DISCARD\", \"EXEC\", \"UNWATCH\"}\n    SLOT_REDIRECT_ERRORS = (AskError, MovedError)\n    CONNECTION_ERRORS = (","sourceCodeStart":3029,"sourceCodeEnd":3065,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L3029-L3065","documentation":"Raised unconditionally by PipelineStrategy.discard() — the non-transactional cluster pipeline strategy. DISCARD aborts a MULTI transaction; a non-transactional cluster pipeline has no transaction to discard, so the call is rejected. To clear a non-transactional pipeline's queued commands, use reset() instead.","triggerScenarios":"Calling await pipe.discard() on a pipeline created via rc.pipeline() or rc.pipeline(transaction=False). Transactional pipelines (transaction=True) support discard.","commonSituations":"Porting standalone pipeline abort logic to cluster mode; calling discard() in error-handling code on the wrong pipeline flavor; expecting DISCARD to clear a non-transactional command queue.","solutions":["For a non-transactional pipeline, call await pipe.reset() to clear queued commands instead of discard().","If you need true transactional DISCARD semantics, use pipe = rc.pipeline(transaction=True).","Restructure error handlers so discard() only runs when transaction=True was used."],"exampleFix":"// before\npipe = rc.pipeline()\nawait pipe.discard()  # raises\n\n// after\npipe = rc.pipeline()\nawait pipe.reset()","handlingStrategy":"validation","validationCode":"async def clear_pipeline(pipe):\n    if getattr(pipe, '_transaction', False):\n        await pipe.discard()\n    else:\n        await pipe.reset()","typeGuard":"from redis.asyncio.cluster import TransactionStrategy\n\ndef supports_discard(pipe) -> bool:\n    return isinstance(getattr(pipe, '_strategy', None), TransactionStrategy)","tryCatchPattern":"from redis.cluster import RedisClusterException\n\ntry:\n    await pipe.discard()\nexcept RedisClusterException as e:\n    if 'discard()' in str(e):\n        await pipe.reset()  # non-transactional equivalent\n    else:\n        raise","preventionTips":["Use reset() to clear a non-transactional pipeline; reserve discard() for transactional ones.","Branch cleanup logic on the pipeline's transaction flag.","Document which pipeline flavor each helper targets."],"tags":["redis-cluster","pipeline","transaction","discard"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}