{"id":"6a35164693400796","repo":"redis/redis-py","slug":"a-type-error-name-occurred-while-watching-o-6a3516","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":"error","filePath":"redis/client.py","lineNumber":1936,"sourceCode":"        \"\"\"\n        if error and failure_count <= conn.retry.get_retries():\n            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        conn.disconnect()\n\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            self.reset()\n            raise WatchError(\n                f\"A {type(error).__name__} occurred while watching one or more keys\"\n            )\n\n    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 = self.connection_pool.get_connection()\n            self.connection = conn\n\n        # Start timing for observability","sourceCodeStart":1918,"sourceCodeEnd":1954,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/client.py#L1918-L1954","documentation":"Raised in `Pipeline._disconnect_reset_raise_on_watching` (the immediate-execution path used for WATCH and pre-MULTI commands). When a retryable connection error occurs while executing a WATCH-time command and retries are exhausted, the connection is disconnected; because the pipeline was in a watching state, the watch is invalidated and a WatchError is raised with the underlying error's type name so the caller knows the transaction must be retried.","triggerScenarios":"Issuing `pipe.watch('k')` followed by a read (e.g. `pipe.get('k')`) on a flaky connection — the immediate_execute_command path is used. If the connection fails after all retries, `_disconnect_reset_raise_on_watching` resets the pipeline and raises `WatchError: A <ErrorType> occurred while watching one or more keys`.","commonSituations":"Network partitions or Redis restarts during an optimistic-locking transaction; connection pool churn under load; misconfigured retry/backoff giving up too early.","solutions":["Wrap WATCH/EXEC blocks in a retry loop that catches WatchError and re-runs the whole transaction.","Increase `Retry(retries=N)` / tune backoff so transient connection errors are absorbed before the watch is invalidated.","Stabilize the connection to the Redis server (network, timeouts, pool size).","Keep the WATCH->read->MULTI->commands->EXEC window short to minimize exposure to disconnects."],"exampleFix":"# before\nwith client.pipeline() as pipe:\n    try:\n        pipe.watch('k')\n        v = pipe.get('k')\n        pipe.multi()\n        pipe.set('k', int(v) + 1)\n        pipe.execute()\n    except WatchError:\n        pass  # silently drops the increment on disconnect\n\n# after\nfor _ in range(10):\n    try:\n        with client.pipeline() as pipe:\n            pipe.watch('k')\n            v = pipe.get('k')\n            pipe.multi()\n            pipe.set('k', int(v) + 1)\n            pipe.execute()\n        break\n    except WatchError:\n        continue","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.exceptions import WatchError\nfor _ in range(5):\n    try:\n        with client.pipeline() as pipe:\n            pipe.watch('k')\n            val = pipe.get('k')\n            pipe.multi()\n            pipe.set('k', transform(val))\n            pipe.execute()\n        break\n    except WatchError:\n        continue  # retry whole transaction","preventionTips":["Always wrap WATCH+EXEC in a retry loop catching WatchError.","Tune Retry(backoff, retries) to absorb transient connection errors.","Keep the WATCH->EXEC window short."],"tags":["pipeline","watch","transaction","connection","retry"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}