This error occurs when EXEC is called without first starting a transaction with MULTI. It typically happens due to connection loss or improper transaction handling in client libraries. Fix it by ensuring MULTI is called before EXEC and using connection pooling.
The "ERR EXEC without MULTI" error means you attempted to execute the EXEC command without first calling MULTI to begin a transaction. In Redis, transactions are started with MULTI, which puts the connection into a transaction mode where subsequent commands are queued instead of executed immediately. When you call EXEC, all queued commands are executed atomically. If EXEC is called outside of this context, Redis rejects it with this error. This error is fundamentally about mismatched transaction boundaries—the server is waiting for MULTI to initiate a transaction block, but received EXEC instead.
Review your code to ensure that every EXEC is preceded by a MULTI command. The basic transaction pattern should always be:
MULTI
[queued commands]
EXECIf you call EXEC without MULTI, Redis cannot process it. Use a try-finally block to ensure proper cleanup:
client.multi();
try {
client.set("key1", "value1");
client.set("key2", "value2");
await client.exec();
} catch (err) {
client.discard();
throw err;
}If using Spring Data Redis, the most common cause is that different connections are used for MULTI and EXEC. Spring Data Redis uses a connection pool, and each command may use a different connection by default. Use SessionCallback to ensure all transaction commands use the same connection:
redisTemplate.execute(new SessionCallback() {
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().set("key1", "value1");
operations.opsForValue().set("key2", "value2");
return operations.exec();
}
});SessionCallback ensures that watch, multi, and exec all run on the same Redis connection, preventing the "EXEC without MULTI" error.
If errors occur during high load or concurrency, your connection pool may be exhausted. Monitor and increase the pool size in your Redis client configuration:
For Jedis (Spring Redis):
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100); // Max connections
config.setMaxIdle(20); // Max idle connections
config.setMinIdle(10); // Min idle connections
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
return config;
}For Node.js Redis:
const client = redis.createClient({
socket: {
maxRetriesPerRequest: null,
enableReadyCheck: false,n },
});For intermittent errors during transient connection issues, implement retry logic:
async function executeTransaction(client, commands) {
let retries = 3;
let lastError;
while (retries > 0) {
try {
const pipeline = client.pipeline();
pipeline.multi();
commands.forEach(cmd => pipeline[cmd.name](...cmd.args));
const result = await pipeline.exec();
return result;
} catch (err) {
lastError = err;
retries--;
if (retries > 0) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, 3 - retries) * 100));
}
}
}
throw lastError;
}The "EXEC without MULTI" error can also occur if the Redis server restarts between MULTI and EXEC. Implement connection monitoring to detect and handle disconnections:
client.on("error", (err) => {
console.error("Redis error:", err);
// Optionally reconnect
});
client.on("reconnecting", () => {
console.log("Attempting to reconnect to Redis...");
});Ensure your client library is configured to automatically reconnect on connection loss. Most modern clients (ioredis, node-redis 4+) handle this by default.
For Redis Cluster deployments, WATCH and MULTI/EXEC have limitations across different slots. In cluster mode, consider using Lua scripts for atomic operations instead of relying on transactions. Lua scripts execute atomically on the server side without the complexity of MULTI/EXEC coordination. Additionally, be aware that Redis does not support transaction rollbacks—if a command fails during EXEC, subsequent commands still execute. 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