redis/redis-rb · error · Redis::SubscriptionError
This client is not subscribed
Error message
This client is not subscribed
What it means
The no-block forms of unsubscribe/punsubscribe (and subscribe-family calls without a block) send the command over @subscription_client — the dedicated pub/sub socket created by a previous subscribe-with-block call. If no subscription is currently active on this instance, @subscription_client is nil and Redis raises SubscriptionError 'This client is not subscribed'.
Source
Thrown at lib/redis.rb:321
end
begin
# The pub/sub second socket is opened via @client.pubsub, which connects through
# ensure_connected rather than a command path. Route it through #synchronize so the same
# RESP3->RESP2 fallback applies when subscribe is the first operation against an old server.
@subscription_client = SubscribedClient.new(synchronize(&:pubsub))
if timeout > 0
@subscription_client.send(method, timeout, *channels, &block)
else
@subscription_client.send(method, *channels, &block)
end
ensure
@subscription_client&.close
@subscription_client = nil
end
else
unless @subscription_client
raise SubscriptionError, "This client is not subscribed"
end
@subscription_client.call_v([method].concat(channels))
end
end
end
require "redis/version"
require "redis/lib_identity"
require "redis/client"
require "redis/pipeline"
require "redis/subscribe"
View on GitHub (pinned to 2ba9010b91)
Solutions
- Drop the defensive unsubscribe: exiting the subscribe block already closes the subscription socket (@subscription_client&.close runs in ensure)
- Send unsubscribe from inside the subscription block, where @subscription_client is guaranteed live
- Guard the call with your own subscription-liveness flag before invoking the no-block form
Example fix
# before
redis.subscribe('news') { |on| on.message { |_, m| process(m) } }
redis.unsubscribe('news') # raises: block exit already closed the subscription
# after
redis.subscribe('news') do |on|
on.message { |_, m| process(m) }
# to stop early, unsubscribe from in here while the subscription is live:
# redis.unsubscribe('news')
end Defensive patterns
Strategy: validation
Validate before calling
def stop_subscription(redis, *channels) redis.unsubscribe(*channels) if @subscribing # app-side flag set around the subscribe block end
Try / catch
begin
redis.unsubscribe('chan')
rescue Redis::SubscriptionError
# already unsubscribed / subscription socket closed — nothing to do
end Prevention
- Send unsubscribe from inside the subscribe block, not after it returns
- Rely on block exit to close the subscription socket — no manual cleanup needed
- Track subscription liveness with an app-side flag around the block
When it happens
Trigger: redis.unsubscribe('chan') or redis.punsubscribe('pattern*') called outside a live subscription block: before any subscribe, after the subscribe block already returned (its ensure closes the socket), or on a fresh client that never subscribed.
Common situations: Defensive cleanup code calling unsubscribe in an ensure block after the subscription loop already exited; shutting down a consumer that never started; a subscription that ended via subscribe_with_timeout and the teardown path still tries to unsubscribe.
Related errors
AI-assisted analysis of redis/redis-rb@2ba9010b91 (2026-08-23).
Data as JSON: /api/errors/54a35f93a71bf530.
Report an issue: GitHub.