This error occurs when you call DISCARD without first starting a transaction with MULTI. DISCARD is only valid within an active transaction context, and calling it outside that context causes Redis to return this error.
In Redis, transactions are initiated with the MULTI command, which puts the connection into transaction mode. During this mode, subsequent commands are queued rather than executed immediately. The DISCARD command is used to abort the transaction and discard all queued commands, restoring the connection to normal state. When you call DISCARD without a corresponding active MULTI command, Redis returns this error because there is no transaction context to abort. This typically happens due to transaction state mismanagement, connection sharing issues, or race conditions in concurrent environments.
Always ensure MULTI has been called on the connection before attempting DISCARD. The proper transaction flow is:
MULTI -> Queue commands -> (EXEC or DISCARD)Never call DISCARD on a connection that is not in transaction mode.
If using connection pooling or sharing connections across multiple threads/requests, ensure proper synchronization. Each logical transaction should use a dedicated connection:
# Bad: shared connection
redis_conn = get_shared_connection()
redis_conn.multi()
# ... another thread uses redis_conn here ...
redis_conn.discard() # Error: DISCARD without MULTI
# Good: connection per transaction
with redis.pipeline(transaction=True) as pipe:
pipe.multi()
pipe.set('key', 'value')
pipe.execute()Wrap transaction logic in try-catch blocks to prevent DISCARD from executing if an exception occurs:
try:
redis_conn.multi()
redis_conn.set('key1', 'value1')
redis_conn.incr('counter')
redis_conn.execute()
except Exception as e:
# DISCARD only if transaction is still active
try:
redis_conn.discard()
except ResponseError:
# Transaction already aborted, connection is clean
pass
raiseModern Redis client libraries provide abstractions that handle MULTI/EXEC/DISCARD automatically:
# Using redis-py pipeline (manages transaction state)
with redis_client.pipeline(transaction=True) as pipe:
pipe.set('key1', 'value1')
pipe.incr('counter')
results = pipe.execute()
# Using Jedis in Java
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
pipeline.watch("key");
pipeline.multi();
pipeline.set("key", "value");
List<Object> results = pipeline.exec(); // Handles DISCARD on watch failure
}Implement logging and monitoring to detect transaction state issues:
import logging
import redis
logger = logging.getLogger(__name__)
class MonitoredRedis:
def __init__(self, client):
self.client = client
self.in_transaction = False
def multi(self):
logger.debug("Starting transaction")
self.in_transaction = True
return self.client.multi()
def discard(self):
if not self.in_transaction:
logger.error("DISCARD called without MULTI")
raise RuntimeError("DISCARD without MULTI")
self.in_transaction = False
return self.client.discard()In multi-threaded environments, consider using Lua scripts instead of MULTI/EXEC. Lua scripts are atomic and eliminate transaction state management complexity: redis.eval("redis.call('set', KEYS[1], ARGV[1]); return redis.call('incr', KEYS[2])", 2, 'key1', 'counter', 'value1'). For distributed systems with connection pooling, prefer client library transaction wrappers (pipelines) that automatically manage transaction context. Note that Redis transactions do not support rollbacksโonce EXEC completes, all commands have been applied. This is by design for performance reasons.
ERR Unbalanced XREAD list of streams
How to fix "ERR Unbalanced XREAD list" in Redis
ERR syntax error
How to fix "ERR syntax error" in Redis
ConnectionError: Error while reading from socket
ConnectionError: Error while reading from socket in redis-py
ERR unknown command
How to fix ERR unknown command in Redis
Command timed out
How to fix 'Command timed out' in ioredis