Error handling
Learn how to handle errors when using Lettuce.
Lettuce uses unchecked exceptions to signal errors. Documentation examples often focus on the "happy path", but production code should catch and handle the exceptions that matter for your workload. This page explains how Lettuce's error handling works and how to apply common error handling patterns.
For an overview of error types and handling strategies, see Error handling. See also Production usage for more information on connection management, timeouts, and other aspects of app reliability.
Exception hierarchy
Lettuce organizes its exceptions under RedisException, which extends
RuntimeException:
"RedisException":
_meta:
description: "Base class for Lettuce runtime exceptions"
"RedisConnectionException":
"RedisCommandTimeoutException":
"RedisCommandInterruptedException":
"RedisCommandExecutionException":
"RedisLoadingException":
"...":
_meta:
ellipsis: true
description: "Other Lettuce exception types"
Key exceptions
The following exceptions are the most commonly encountered in Lettuce applications. See Categories of errors for a more detailed discussion of these errors and their causes.
| Exception | When it occurs | Recoverable | Recommended action |
|---|---|---|---|
RedisConnectionException |
Connection setup failed or the connection was lost | ✅ | Retry with backoff or fall back |
RedisCommandTimeoutException |
A command exceeded its timeout | ✅ | Retry with backoff and review timeout settings |
RedisCommandExecutionException |
Redis returned an error reply such as WRONGTYPE |
❌ | Fix the command, arguments, or data model |
RedisCommandInterruptedException |
A waiting thread was interrupted while waiting for a result | ⚠️ | Restore interrupt status and abort cleanly |
Applying error handling patterns
The Error handling overview describes four main patterns. The sections below show how to implement them in Lettuce:
Pattern 1: Fail fast
Catch specific exceptions that represent unrecoverable errors and re-throw them (see Pattern 1: Fail fast for a full description):
try {
return commands.get(key);
} catch (RedisCommandExecutionException e) {
// This indicates a bug in our code or data model.
throw e;
}
Pattern 2: Graceful degradation
Catch temporary connectivity failures and fall back to an alternative (see Pattern 2: Graceful degradation for a full description):
try {
String cachedValue = commands.get(key);
if (cachedValue != null) {
return cachedValue;
}
} catch (RedisConnectionException e) {
logger.warn("Cache unavailable, using database");
}
return database.get(key);
Pattern 3: Retry with backoff
Retry on temporary errors such as timeouts or disconnections (see Pattern 3: Retry with backoff for a full description):
int maxRetries = 3;
long delayMs = 100;
for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
return commands.get(key);
} catch (RedisConnectionException | RedisCommandTimeoutException e) {
if (attempt == maxRetries - 1) {
throw e;
}
try {
Thread.sleep(delayMs);
delayMs *= 2; // Exponential backoff
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException(ie);
}
}
}
throw new IllegalStateException("unreachable");
Pattern 4: Log and continue
Log non-critical cache failures and continue (see Pattern 4: Log and continue for a full description):
try {
commands.setex(key, 3600, value);
} catch (RedisConnectionException | RedisCommandTimeoutException e) {
logger.warn("Failed to cache {}, continuing without cache", key, e);
}
Async and reactive error handling
Lettuce's async and reactive APIs report the same underlying exceptions, but
they usually surface them through CompletionStage failures or stream errors
instead of direct throw statements:
async.get(key).whenComplete((value, error) -> {
if (error == null) {
use(value);
return;
}
Throwable cause = error.getCause() != null ? error.getCause() : error;
if (cause instanceof RedisConnectionException) {
logger.warn("Cache unavailable");
return;
}
throw new RuntimeException(cause);
});