{"id":"13ac6c5b43fd0daf","repo":"redis/redis-py","slug":"wrong-number-of-response-items-from-pipeline-execu","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/asyncio/client.py","lineNumber":2082,"sourceCode":"        except ExecAbortError as err:\n            if errors:\n                raise errors[0][1] from err\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.\") from None\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            if self.connection:\n                await self.connection.disconnect()\n            raise ResponseError(\n                \"Wrong number of response items from pipeline execution\"\n            ) from None\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                command_name = args[0]\n\n                # Remove keys entry, it needs only for cache.\n                options.pop(\"keys\", None)\n\n                if command_name in self.response_callbacks:","sourceCodeStart":2064,"sourceCodeEnd":2100,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/client.py#L2064-L2100","documentation":"Raised by Pipeline._execute_transaction (redis/asyncio/client.py:2082) when the number of items in the EXEC response does not equal the number of queued commands. This indicates a protocol desync — the client and server disagree about how many replies are on the wire. The library disconnects the connection (to reset protocol state) and raises ResponseError.","triggerScenarios":"A corrupted or out-of-sync connection where EXEC returns the wrong arity: typically a library/server bug, a partial command drop, or commands that the server rejected in a way that changed reply count. This is a defensive check, not normal control flow.","commonSituations":"Extremely rare in normal use; seen with intermediary proxies that mis-shape transaction replies, custom response callbacks that consume extra frames, or version skew between client and server on a command's reply shape.","solutions":["Report it as a bug: capture redis-py version, server version, and the exact command stack.","Ensure no custom response_callback consumes/returns multiple frames for a single command.","Disconnect and rebuild the client (the library already disconnects the offending connection).","Test against a direct Redis node rather than a proxy/gateway to isolate the cause."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.exceptions import ResponseError\ntry:\n    await pipe.execute()\nexcept ResponseError as e:\n    if 'Wrong number of response items' in str(e):\n        log.exception('pipeline protocol desync; reconnecting')\n        await r.connection_pool.disconnect()\n    raise","preventionTips":["Keep response_callbacks minimal and single-frame per command.","Report occurrences with redis-py and server versions — this signals a real bug.","Avoid proxies that rewrite transaction replies."],"tags":["redis","asyncio","pipeline","transactions","protocol-desync","defensive"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}