{"record":{"id":"54c241aacffd65da","repo":"redis/redis-py","slug":"unexpected-response-length-for-cluster-pipeline-ex","errorCode":null,"errorMessage":"Unexpected response length for cluster pipeline EXEC. Command stack was {} but response had length {}","messagePattern":"Unexpected response length for cluster pipeline EXEC\\. Command stack was (.+?) but response had length (.+?)","errorType":"exception","errorClass":"InvalidPipelineStack","httpStatus":null,"severity":"critical","filePath":"redis/asyncio/cluster.py","lineNumber":3462,"sourceCode":"        except ExecAbortError:\n            if errors:\n                raise errors[0]\n            raise\n\n        self._executing = False\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(self._command_queue):\n            raise InvalidPipelineStack(\n                \"Unexpected response length for cluster pipeline EXEC.\"\n                \" Command stack was {} but response had length {}\".format(\n                    [c.args[0] for c in self._command_queue], len(response)\n                )\n            )\n\n        # find any errors in the response and raise if necessary\n        if raise_on_error or len(errors) > 0:\n            await self._raise_first_error(\n                response,\n                self._command_queue,\n                start_time,\n            )\n\n        # We have to run response callbacks manually\n        data = []\n        for r, cmd in zip(response, self._command_queue):\n            if not isinstance(r, Exception):","sourceCodeStart":3444,"sourceCodeEnd":3480,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/cluster.py#L3444-L3480","documentation":"InvalidPipelineStack raised in TransactionStrategy._execute_transaction (redis/asyncio/cluster.py:3462) when the EXEC response array length does not equal the buffered command queue length. This indicates a protocol desynchronization between what was queued and what the server returned - typically an internal library bug, a connection that silently dropped a command, or mixed protocol/server behavior. It should not occur in normal use.","triggerScenarios":"A corruption of the pipeline command queue; a RESP framing desync; an unexpected server-side abort that changed the response count; library version mismatch with server behavior.","commonSituations":"Hitting an edge case after a reconnect mid-transaction; using commands whose response shape the library mis-counts; concurrency bugs sharing a pipeline across tasks.","solutions":["Ensure a ClusterPipeline is not shared/used concurrently across asyncio tasks","Upgrade redis-py to the latest patch release - this is usually an internal bug","Reproduce with debug logging and file an issue with the command stack from the error message","Reset the pipeline and retry the transaction from scratch"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"# internal invariant; cannot be prevented by callers. Avoid sharing a pipeline across tasks.","typeGuard":null,"tryCatchPattern":"try:\n    await pipe.execute()\nexcept InvalidPipelineStack:\n    await pipe.reset()\n    # rebuild and retry once; if it recurs, upgrade redis-py and report","preventionTips":["Never share a ClusterPipeline across concurrent asyncio tasks","Keep redis-py up to date - this usually signals an internal bug","Reset and retry the transaction once on this error"],"tags":["cluster","pipeline","transaction","protocol","internal-bug"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}