{"record":{"id":"f636f0441187549b","repo":"redis/redis-py","slug":"wrong-number-of-response-items-from-pipeline-execu-f636f0","errorCode":null,"errorMessage":"Wrong number of response items from pipeline execution","messagePattern":"Wrong number of response items from pipeline execution","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"redis/client.py","lineNumber":2069,"sourceCode":"            response = self.parse_response(connection, \"_\")\n        except ExecAbortError:\n            if errors:\n                raise errors[0][1]\n            raise\n\n        # EXEC clears any watched keys\n        self.watching = False\n\n        if response is None:\n            raise WatchError(\"Watched variable changed.\")\n\n        # put any parse errors into the response\n        for i, e in errors:\n            response.insert(i, e)\n\n        if len(response) != len(commands):\n            self.connection.disconnect()\n            raise ResponseError(\n                \"Wrong number of response items from pipeline execution\"\n            )\n\n        # find any errors in the response and raise if necessary\n        if raise_on_error:\n            self.raise_first_error(commands, response)\n\n        # We have to run response callbacks manually\n        data = []\n        for r, cmd in zip(response, commands):\n            if not isinstance(r, Exception):\n                args, options = cmd\n                # Remove keys entry, it needs only for cache.\n                options.pop(\"keys\", None)\n                command_name = args[0]\n                if command_name in self.response_callbacks:\n                    r = self.response_callbacks[command_name](r, **options)\n            data.append(r)","sourceCodeStart":2051,"sourceCodeEnd":2087,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/client.py#L2051-L2087","documentation":"Raised as ResponseError from _execute_transaction when the number of items in the EXEC response does not equal the number of queued commands. This indicates the client and server disagree on the transaction contents — a protocol-level corruption or a server-side abort that truncated results. The connection is forcibly disconnected because its state is now unreliable.","triggerScenarios":"The command stack length and the EXEC reply length diverge. Causes: a command in the stack was rejected at queue time but still counted; RESP framing corruption from a proxy/intermediary; mixed RESP2/RESP3 oddities; bugs in custom response callbacks that mutate the response list.","commonSituations":"A Redis proxy (Twemproxy, Redis Cluster proxy, Envoy) that rewrites MULTI/EXEC; corrupted connections from a load balancer; running an old redis-py against a newer Redis with changed command arity; pipeline reuse after a partial failure.","solutions":["Do not reuse a pipeline after a failure — create a fresh one each transaction.","Bypass any intermediary proxy that does not faithfully forward MULTI/EXEC framing; connect directly to a Redis node.","Verify Redis and redis-py versions are compatible for the commands in the stack.","Capture the command stack and response lengths (logged in the error path) to identify the divergent command, and file a bug if reproducible against a real Redis.","Ensure no custom command mutates the response list unexpectedly."],"exampleFix":"// before (reused pipeline after error)\npipe = r.pipeline(transaction=True)\npipe.set('a', 1)\ntry:\n    pipe.execute()\nexcept Exception:\n    pass\npipe.set('b', 2)\npipe.execute()  # state may be inconsistent -> ResponseError\n\n// after (fresh pipeline per transaction)\nwith r.pipeline(transaction=True) as pipe:\n    pipe.set('a', 1)\n    pipe.execute()","handlingStrategy":"try-catch","validationCode":"# Pre-check: ensure the command stack matches what you intend and the pipeline is fresh.\nassert not pipe.connection or not getattr(pipe.connection, '_desynced', False), \\\n    'pipeline/connection previously failed; create a new pipeline'","typeGuard":"def pipeline_is_fresh(pipe) -> bool:\n    return len(pipe.command_stack) == 0 and pipe.connection is None","tryCatchPattern":"from redis.exceptions import ResponseError\ntry:\n    pipe.execute()\nexcept ResponseError as e:\n    if 'Wrong number of response items' in str(e):\n        # connection was disconnected by the library; start a fresh pipeline\n        pipe = r.pipeline(transaction=True)\n    else:\n        raise","preventionTips":["Never reuse a pipeline after any failure; create a new one.","Avoid proxies that rewrite MULTI/EXEC framing; connect directly to Redis.","Keep Redis and redis-py versions aligned for the commands you queue."],"tags":["pipeline","transaction","protocol","response","corruption"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}