The ioredis 'Command timed out' error occurs when a Redis command fails to return a response within the configured timeout period. This typically indicates network issues, slow Redis operations, or insufficient timeout configuration.
The "Command timed out" error in ioredis means that a Redis command did not return a reply within the specified number of milliseconds set in the client configuration. By default, ioredis uses the `commandTimeout` option to determine how long to wait for a response before throwing this error. When you execute a Redis command through ioredis, the client sends the request to the Redis server and waits for a response. If the response doesn't arrive within the timeout period, ioredis throws a "Command timed out" error to prevent your application from hanging indefinitely. This error is particularly common during high load scenarios, when executing long-running commands (like KEYS * on large datasets), or when network latency between your application and Redis server is high. Unlike connection errors, this indicates the connection was established but the specific command execution took too long.
If your legitimate operations need more time, increase the timeout:
const Redis = require('ioredis');
const redis = new Redis({
host: 'localhost',
port: 6379,
commandTimeout: 5000, // Default is undefined (no timeout)
// Increase to 10 seconds if needed
commandTimeout: 10000,
});Note: Only increase timeout if you have legitimate long-running operations. Don't mask underlying performance issues with very high timeouts.
By default, ioredis retries commands 20 times before failing. Adjust this based on your needs:
const redis = new Redis({
host: 'localhost',
port: 6379,
// Limit retries to fail faster
maxRetriesPerRequest: 3,
// Or set to null to retry indefinitely (not recommended for most cases)
// maxRetriesPerRequest: null,
});Setting this lower helps your application fail faster instead of queuing commands during connectivity issues.
Identify if Redis is slow or overloaded:
# Connect to Redis CLI
redis-cli
# Check slow log for commands taking too long
SLOWLOG GET 10
# Check current connections and memory
INFO stats
INFO memory
# Monitor commands in real-time
MONITORLook for:
- Commands taking longer than expected in SLOWLOG
- High memory usage or evictions
- Unusual command patterns
Replace blocking operations with more efficient alternatives:
// BAD: KEYS blocks Redis
const keys = await redis.keys('user:*');
// GOOD: SCAN doesn't block
const stream = redis.scanStream({
match: 'user:*',
count: 100,
});
const keys = [];
stream.on('data', (resultKeys) => {
keys.push(...resultKeys);
});
stream.on('end', () => {
console.log('All keys:', keys);
});Other optimizations:
- Use SSCAN instead of SMEMBERS for large sets
- Use pipelining for multiple commands
- Use Lua scripts to reduce round trips
For high-traffic applications, configure connection pooling:
const Redis = require('ioredis');
// For standalone Redis
const redis = new Redis({
host: 'localhost',
port: 6379,
enableOfflineQueue: false, // Fail fast if disconnected
connectTimeout: 10000, // 10 seconds to establish connection
commandTimeout: 5000, // 5 seconds for command execution
retryStrategy(times) {
const delay = Math.min(times * 50, 2000);
return delay;
},
});
// For cluster mode
const cluster = new Redis.Cluster(
[{ host: 'redis-1', port: 6379 }],
{
redisOptions: {
commandTimeout: 5000,
},
clusterRetryStrategy(times) {
return Math.min(100 * times, 2000);
},
}
);Verify network latency between your application and Redis:
# Test network latency
ping your-redis-host
# Test Redis response time
redis-cli -h your-redis-host --latency
# Test continuous latency
redis-cli -h your-redis-host --latency-history
# Check for packet loss
redis-cli -h your-redis-host --latency-distIf latency is consistently high (>50ms), consider:
- Moving Redis closer to your application (same data center/region)
- Using Redis Cluster for geo-distributed setups
- Investigating network infrastructure issues
Handle timeouts gracefully in your application:
const Redis = require('ioredis');
const redis = new Redis({
host: 'localhost',
port: 6379,
commandTimeout: 5000,
maxRetriesPerRequest: 3,
});
// Handle timeout errors
async function getWithRetry(key, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await redis.get(key);
} catch (error) {
if (error.message.includes('Command timed out')) {
console.log(`Retry ${i + 1}/${maxRetries} for key: ${key}`);
if (i === maxRetries - 1) throw error;
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 100 * (i + 1)));
} else {
throw error;
}
}
}
}
// Use circuit breaker pattern for resilience
redis.on('error', (error) => {
console.error('Redis error:', error);
});Disk I/O from persistence can cause command delays:
# Check current Redis configuration
redis-cli CONFIG GET save
redis-cli CONFIG GET appendonly
# Disable persistence if you don't need durability (cache use case)
redis-cli CONFIG SET save ""
redis-cli CONFIG SET appendonly no
# Or tune persistence for better performance
# Background save every 5 minutes if at least 100 keys changed
redis-cli CONFIG SET save "300 100"In redis.conf:
# Use appendfsync everysec instead of always for better performance
appendfsync everysec
# Enable background rewrite
no-appendfsync-on-rewrite yes### Understanding ioredis Timeout Behavior
ioredis has multiple timeout settings that serve different purposes:
- `connectTimeout`: Time to wait for initial connection establishment (default: 10 seconds)
- `commandTimeout`: Time to wait for command response (default: undefined/no timeout)
- `maxRetriesPerRequest`: Number of times to retry a command before failing (default: 20)
When commandTimeout is not set, commands can wait indefinitely, which may cause application hangs. However, setting it too low can cause false positives during temporary slowdowns.
### Cluster Mode Considerations
In Redis Cluster mode, timeouts can be more complex:
- Commands may timeout during cluster resharding
- MOVED/ASK redirects add latency
- Some commands (MGET, MSET) may need to query multiple nodes
Configure cluster-specific timeouts:
const cluster = new Redis.Cluster(nodes, {
clusterRetryStrategy(times) {
return Math.min(100 * times, 2000);
},
redisOptions: {
commandTimeout: 10000, // Higher timeout for cluster
},
});### Production Best Practices
1. Set reasonable defaults: Start with 5-10 second commandTimeout
2. Monitor timeout rates: Track what percentage of commands timeout
3. Use connection pooling: Don't create new clients for each request
4. Implement circuit breakers: Fail fast when Redis is consistently slow
5. Add alerting: Get notified when timeout rates exceed thresholds
### Performance Profiling
Use ioredis's built-in monitoring:
redis.on('ready', () => {
console.log('Redis ready');
});
redis.on('reconnecting', (delay) => {
console.log(`Reconnecting in ${delay}ms`);
});
redis.on('wait', (duration) => {
console.log(`Command waited ${duration}ms`);
});### Serverless/Lambda Considerations
In serverless environments, timeouts are more common due to:
- Cold starts creating new connections
- VPC networking adding latency
- Function timeout limits
Solutions:
- Use connection pooling with ioredis-mock for testing
- Keep connections warm with periodic pings
- Set aggressive commandTimeout to stay under function limits
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
ERR DISCARD without MULTI
How to fix "ERR DISCARD without MULTI" in Redis