redis/redis-rb · error · Redis::SubscriptionError
Can't unsubscribe if not subscribed.
Error message
Can't unsubscribe if not subscribed.
What it means
Redis::Distributed tracks at most one active subscription via @subscribed_node, set by #subscribe and nil otherwise. #unsubscribe forwards to that node, so calling it when @subscribed_node is nil raises Redis::SubscriptionError ("Can't unsubscribe if not subscribed.") — the instance has no subscription to tear down. The same error class and message are used by the standalone client's subscription loop, so it predates Distributed. On Distributed it is purely a client-side state check: nothing is sent to any server.
Source
Thrown at lib/redis/distributed.rb:1281
!!@subscribed_node
end
# Listen for messages published to the given channels.
def subscribe(channel, *channels, &block)
if channels.empty?
@subscribed_node = node_for(channel)
@subscribed_node.subscribe(channel, &block)
else
ensure_same_node(:subscribe, [channel] + channels) do |node|
@subscribed_node = node
node.subscribe(channel, *channels, &block)
end
end
end
# Stop listening for messages posted to the given channels.
def unsubscribe(*channels)
raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
@subscribed_node.unsubscribe(*channels)
end
# Listen for messages published to channels matching the given patterns.
# See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
# for further details
def psubscribe(*channels, &block)
raise NotImplementedError
end
# Stop listening for messages posted to channels matching the given
# patterns.
# See the [Redis Server PUNSUBSCRIBE documentation](https://redis.io/docs/latest/commands/punsubscribe/)
# for further details
def punsubscribe(*channels)
raise NotImplementedError
endView on GitHub (pinned to 2ba9010b91)
Solutions
- Guard the call: dist.unsubscribe(*channels) if dist.subscribed?
- Make the unsubscribe path use the same Distributed instance that subscribed (pass the client through, or store it next to the subscription)
- For cleanup blocks, rescue Redis::SubscriptionError and treat it as already-clean
Example fix
# before
def shutdown(dist)
dist.unsubscribe("news")
end
# => Redis::SubscriptionError: Can't unsubscribe if not subscribed.
# after
def shutdown(dist)
dist.unsubscribe("news") if dist.subscribed?
end Defensive patterns
Strategy: validation
Validate before calling
# Distributed exposes subscribed? exactly for this check dist.unsubscribe(*channels) if dist.subscribed?
Type guard
def can_unsubscribe?(client) !client.respond_to?(:subscribed?) || client.subscribed? end
Try / catch
begin dist.unsubscribe(*channels) rescue Redis::SubscriptionError # already unsubscribed — nothing to do end
Prevention
- Always pair subscribe and unsubscribe on the same Distributed instance; store the client next to the subscription state
- Guard every cleanup/ensure path that touches subscriptions with subscribed?
- Remember subscribe with a block blocks until the subscription ends — code after the block does not need to unsubscribe at all
- In shutdown hooks, treat SubscriptionError as benign
When it happens
Trigger: Calling dist.unsubscribe(*channels) before any dist.subscribe on that instance; after the subscribe block has already returned (subscription already closed); after an error interrupted the subscribe loop; in an ensure/cleanup block that runs whether or not a subscription exists; calling unsubscribe on a different Redis::Distributed instance than the one that subscribed.
Common situations: Worker shutdown hooks (SIGTERM handlers) that unconditionally unsubscribe; drain/cleanup code in ensure blocks; reconnect logic that tears down subscriptions it never established; multi-process forking where the child inherits the object but not the subscription state.
Related errors
- UNWATCH cannot be used in Redis::Distributed because the key
- This client is not subscribed
- MAPPED_MSETNX cannot be used in Redis::Distributed because t
- JSON_MSET cannot be used in Redis::Distributed because the k
- PIPELINED cannot be used in Redis::Distributed because the k
AI-assisted analysis of redis/redis-rb@2ba9010b91 (2026-08-23).
Data as JSON: /api/errors/0da713a08cbbaf44.
Report an issue: GitHub.