The Error
You run a Redis command โ SET, GET, HSET, PUBLISH, or anything else โ and get this back:
(error) ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT / RESET allowed in this context
That connection is stuck in Pub/Sub subscriber mode. Once a connection calls SUBSCRIBE or PSUBSCRIBE, Redis locks it down hard. It will only accept subscriber-related commands until it exits that mode. Everything else โ including PUBLISH โ triggers this error.
Why This Happens
Pub/Sub mode is connection-scoped. The moment a client sends SUBSCRIBE channel, that TCP connection enters a dedicated listening state. Redis blocks all other commands on purpose โ it needs to guarantee message delivery without interference from writes or reads on the same socket.
Common scenarios where this trips people up:
- Reusing the same Redis client instance for both subscribing and publishing/writing
- A connection pool that hands out an already-subscribed connection to regular commands
- Calling
PUBLISHon the subscriber connection โ a very common mistake, sincePUBLISHis a write command, not a subscriber command - Forgetting to unsubscribe before reusing a connection
The Fix
Subscriber connections must be dedicated. Full stop. Use one connection exclusively for SUBSCRIBE/PSUBSCRIBE, and a completely separate connection for everything else โ including PUBLISH.
Fix in redis-cli
Open two terminals โ one per role:
# Terminal 1 โ subscriber only
redis-cli
127.0.0.1:6379> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit)
# Terminal 2 โ publisher / regular commands
redis-cli
127.0.0.1:6379> PUBLISH news "hello"
(integer) 1
127.0.0.1:6379> SET foo bar
OK
To exit Pub/Sub mode on the subscriber side, press Ctrl-C, or send UNSUBSCRIBE. On Redis 6.2+, RESET also works.
Fix in Python (redis-py)
redis-py ships with a PubSub object that manages its own connection under the hood โ exactly so you don't accidentally mix them:
import redis
import threading
r = redis.Redis(host='localhost', port=6379)
# Subscriber โ dedicated pubsub object (its own connection)
def message_handler(message):
print(f"Received: {message['data']}")
pubsub = r.pubsub()
pubsub.subscribe(**{'news': message_handler})
# Run subscriber in background thread
thread = pubsub.run_in_thread(sleep_time=0.01)
# Main connection โ use r for everything else
r.set('foo', 'bar') # OK โ uses main connection
r.publish('news', 'hello') # OK โ uses main connection, not pubsub
thread.stop()
Where people go wrong โ using a raw connection object directly:
# This is fine โ r and pubsub are separate objects
pubsub = r.pubsub()
pubsub.subscribe('news')
r.set('foo', 'bar') # OK
# This breaks:
raw_conn = r.connection_pool.get_connection('_')
raw_conn.send_command('SUBSCRIBE', 'news')
raw_conn.send_command('SET', 'foo', 'bar') # ERR only (P)SUBSCRIBE...
Fix in Node.js (ioredis)
Instantiate two separate clients โ one for subscribing, one for everything else:
const Redis = require('ioredis');
const subscriber = new Redis();
const publisher = new Redis();
subscriber.subscribe('news', (err, count) => {
if (err) throw err;
console.log(`Subscribed to ${count} channel(s)`);
});
subscriber.on('message', (channel, message) => {
console.log(`[${channel}] ${message}`);
});
// publisher handles all writes and PUBLISH calls
publisher.publish('news', 'hello');
publisher.set('foo', 'bar');
Fix in Java (Jedis)
JedisPool pool = new JedisPool("localhost", 6379);
// Subscriber โ blocks the thread, needs a dedicated connection
new Thread(() -> {
try (Jedis jedis = pool.getResource()) {
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println(channel + ": " + message);
}
}, "news");
}
}).start();
// Normal commands โ grab a separate connection from the pool
try (Jedis jedis = pool.getResource()) {
jedis.publish("news", "hello");
jedis.set("foo", "bar");
}
Resetting a stuck subscriber connection (Redis 6.2+)
Got a connection stuck in Pub/Sub mode that you want to reuse? RESET brings it straight back to normal โ no need to close and reopen the socket:
127.0.0.1:6379> SUBSCRIBE news
...
127.0.0.1:6379> RESET
+RESET
On Redis versions before 6.2, UNSUBSCRIBE from every channel first, or just close the connection and open a fresh one.
Verify the Fix
- Run your subscriber logic โ confirm it receives messages cleanly.
- On the separate connection, run a write:
SET test okshould returnOK. - Publish from that same connection:
PUBLISH news "ping"should return an integer (the subscriber count), not an error. - Check that your subscriber actually receives the message โ if it does, both connections are healthy.
# Quick sanity check across two terminals
# Terminal 1
redis-cli SUBSCRIBE test-channel
# Terminal 2
redis-cli PUBLISH test-channel "works"
# Expected: (integer) 1
# Terminal 1 should print:
# 1) "message"
# 2) "test-channel"
# 3) "works"
Quick Reference
Commands allowed on a subscriber connection:
SUBSCRIBE/UNSUBSCRIBEโ manage channel subscriptionsPSUBSCRIBE/PUNSUBSCRIBEโ manage pattern subscriptionsPINGโ keepalive check (returns a special Pub/Sub pong, not the usual+PONG)QUITโ close the connectionRESETโ exit Pub/Sub mode (Redis 6.2+)
Everything else โ GET, SET, PUBLISH, HSET, EXPIRE, all of it โ must go through a non-subscriber connection.

