{"id":"602f64df4fb77554","repo":"redis/redis-py","slug":"a-type-error-name-occurred-while-watching-o","errorCode":null,"errorMessage":"A {type(error).__name__} occurred while watching one or more keys","messagePattern":"A (.+?) occurred while watching one or more keys","errorType":"exception","errorClass":"WatchError","httpStatus":null,"severity":"warning","filePath":"redis/asyncio/client.py","lineNumber":1947,"sourceCode":"            and failure_count is not None\n            and failure_count <= conn.retry.get_retries()\n        ):\n            await record_operation_duration(\n                command_name=command_name,\n                duration_seconds=time.monotonic() - start_time,\n                server_address=getattr(conn, \"host\", None),\n                server_port=getattr(conn, \"port\", None),\n                db_namespace=str(conn.db),\n                error=error,\n                retry_attempts=failure_count,\n            )\n        await conn.disconnect(error=error, failure_count=failure_count)\n        # if we were already watching a variable, the watch is no longer\n        # valid since this connection has died. raise a WatchError, which\n        # indicates the user should retry this transaction.\n        if self.watching:\n            await self.reset()\n            raise WatchError(\n                f\"A {type(error).__name__} occurred while watching one or more keys\"\n            )\n\n    async def immediate_execute_command(self, *args, **options):\n        \"\"\"\n        Execute a command immediately, but don't auto-retry on the supported\n        errors for retry if we're already WATCHing a variable.\n        Used when issuing WATCH or subsequent commands retrieving their values but before\n        MULTI is called.\n        \"\"\"\n        command_name = args[0]\n        conn = self.connection\n        # if this is the first call, we need a connection\n        if not conn:\n            conn = await self.connection_pool.get_connection()\n            self.connection = conn\n\n        # Start timing for observability","sourceCodeStart":1929,"sourceCodeEnd":1965,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/client.py#L1929-L1965","documentation":"Raised by Pipeline._disconnect_reset_raise_on_watching (redis/asyncio/client.py:1947) when a connection error occurs during a WATCH-protected transaction. Because the connection died, any WATCH is invalidated; the library disconnects, resets watching state, and raises WatchError wrapping the underlying error type name so the caller knows to retry the whole transaction.","triggerScenarios":"A network error, timeout, or reconnectable server condition (the retry-supported errors) exhausts retries while self.watching is True inside a pipeline transaction. The failure_callback in immediate_execute_command routes here.","commonSituations":"Transient network blips or server failovers during an optimistic-locking transaction; socket timeouts under load while WATCH is active.","solutions":["Treat WatchError as a retry signal: re-WATCH, re-queue, and re-execute the transaction in a loop.","Increase retry count / backoff so transient errors are absorbed before reaching this handler.","Shorten the WATCH-to-EXEC window to reduce exposure to connection drops."],"exampleFix":"// before\npipe = r.pipeline(transaction=True)\nawait pipe.watch('k')\nawait pipe.multi()\nawait pipe.set('k', 'v')\nawait pipe.execute()\n// after\nfor _ in range(MAX_RETRIES):\n    try:\n        pipe = r.pipeline(transaction=True)\n        await pipe.watch('k')\n        await pipe.multi()\n        await pipe.set('k', 'v')\n        await pipe.execute()\n        break\n    except redis.exceptions.WatchError:\n        continue","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.exceptions import WatchError\nfor _ in range(MAX_RETRIES):\n    try:\n        pipe = r.pipeline(transaction=True)\n        await pipe.watch('k')\n        await pipe.multi()\n        await pipe.set('k', 'v')\n        await pipe.execute()\n        break\n    except WatchError:\n        continue","preventionTips":["Treat WatchError as a retry trigger, not a fatal error.","Configure retry/backoff to absorb transient connection errors before they reach the watch handler.","Keep the WATCH-to-EXEC window short."],"tags":["redis","asyncio","pipeline","transactions","watch","retry","connection"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}