Description
When a Redis Cluster node connection fails (timeout or server-closed connection), redis-py attempts to emit the after_command_execution event. During this process it assumes the connection object has a .db attribute.
However, in some error paths the object passed is a ClusterNode rather than a Connection. This results in a secondary exception:
AttributeError: 'ClusterNode' object has no attribute 'db'
This masks the original Redis connectivity error and causes application code that correctly catches RedisError to still crash.
Observed behavior
A real connection error occurs first:
redis.exceptions.ConnectionError: Connection closed by server.
...
redis.exceptions.TimeoutError: Timeout connecting to server
But redis-py then throws:
AttributeError: 'ClusterNode' object has no attribute 'db'
Traceback excerpt:
File ".../redis/cluster.py", line 1755, in _emit_after_command_execution_event
db_namespace=str(connection.db),
AttributeError: 'ClusterNode' object has no attribute 'db'
Because this is not a RedisError, it escapes application error handling.
Expected behavior
If Redis cluster connectivity fails, redis-py should raise a RedisError subclass (ConnectionError, TimeoutError, etc.) and not emit secondary exceptions from observability/event hooks.
Event emission should handle missing attributes safely.
Versions
Working:
Broken:
Python:
Environment
- Redis Cluster
- Django application
- redis-py
RedisCluster
- cluster nodes configured via
startup_nodes
Example initialization:
from redis.cluster import RedisCluster, ClusterNode
nodes = [
ClusterNode("host1", 6379),
ClusterNode("host2", 6379),
]
client = RedisCluster(
startup_nodes=nodes,
username="...",
password="...",
socket_connect_timeout=5,
socket_timeout=3,
)
Minimal failing example
The issue occurs during a normal command when the cluster node cannot be reached:
If the node is unreachable or times out, the above command may produce:
AttributeError: 'ClusterNode' object has no attribute 'db'
instead of a Redis exception.
Workaround
Application code must currently catch AttributeError in addition to RedisError:
try:
client.get("key")
except (RedisError, AttributeError):
...
This is undesirable because AttributeError should not be part of the public API surface.
Suspected cause
The cluster error path passes a ClusterNode object as connection to _emit_after_command_execution_event, which assumes a Connection object and accesses connection.db.
Example location in traceback:
_emit_after_command_execution_event(...)
db_namespace=str(connection.db)
ClusterNode does not expose .db.
Impact
- Masks the original Redis connection failure
- Breaks error handling that correctly catches
RedisError
- Can crash production services on transient cluster connectivity issues
Suggested fix
Guard access to connection.db in _emit_after_command_execution_event, for example:
db_namespace = getattr(connection, "db", None)
or ensure only Connection objects are passed to the event emitter.
Description
When a Redis Cluster node connection fails (timeout or server-closed connection), redis-py attempts to emit the
after_command_executionevent. During this process it assumes theconnectionobject has a.dbattribute.However, in some error paths the object passed is a
ClusterNoderather than aConnection. This results in a secondary exception:This masks the original Redis connectivity error and causes application code that correctly catches
RedisErrorto still crash.Observed behavior
A real connection error occurs first:
But redis-py then throws:
Traceback excerpt:
Because this is not a
RedisError, it escapes application error handling.Expected behavior
If Redis cluster connectivity fails, redis-py should raise a
RedisErrorsubclass (ConnectionError,TimeoutError, etc.) and not emit secondary exceptions from observability/event hooks.Event emission should handle missing attributes safely.
Versions
Working:
Broken:
Python:
Environment
RedisClusterstartup_nodesExample initialization:
Minimal failing example
The issue occurs during a normal command when the cluster node cannot be reached:
If the node is unreachable or times out, the above command may produce:
instead of a Redis exception.
Workaround
Application code must currently catch
AttributeErrorin addition toRedisError:This is undesirable because
AttributeErrorshould not be part of the public API surface.Suspected cause
The cluster error path passes a
ClusterNodeobject asconnectionto_emit_after_command_execution_event, which assumes aConnectionobject and accessesconnection.db.Example location in traceback:
ClusterNodedoes not expose.db.Impact
RedisErrorSuggested fix
Guard access to
connection.dbin_emit_after_command_execution_event, for example:or ensure only
Connectionobjects are passed to the event emitter.