content/develop/clients/error-handling.md
When working with Redis, errors can occur for various reasons, including network issues, invalid commands, or resource constraints. This guide explains the types of errors you might encounter and how to handle them effectively.
Redis errors fall into four main categories. The table below provides a quick overview of each type. Click on any error type to jump to its detailed section, which includes common causes, examples, handling strategies, and code examples.
| Error Type | Common Causes | When to Handle | Examples |
|---|---|---|---|
| Connection errors | Network issues, server down, auth failure, timeouts, pool exhaustion | Almost always | ConnectionError, TimeoutError, AuthenticationError |
| Command errors | Typo in command, wrong arguments, invalid types, unsupported command | Rarely (usually indicates a bug) | ResponseError, WRONGTYPE, ERR unknown command |
| Data errors | Serialization failures, corrupted data, type mismatches | Sometimes (depends on data source) | JSONDecodeError, SerializationError, WRONGTYPE |
| Resource errors | Memory limit, pool exhausted, too many connections, key eviction | Sometimes (some are temporary) | OOM, pool timeout, LOADING |
Connection errors occur when your application cannot communicate with Redis. These are typically temporary and often recoverable.
Common causes:
Examples:
ConnectionError: Network failure or server unreachableTimeoutError: Operation exceeded the configured timeoutAuthenticationError: Invalid credentialsWhen to handle: Almost always. Connection errors are usually temporary, so implementing retry logic or fallback strategies is recommended.
Example strategy:
graph TB
A["Try to connect
to Redis"]
A -->|Success| B(["Use the result"])
A -->|Failure| C{Error type?}
C -->|Timeout| D(["Retry with
exponential backoff"])
C -->|Auth failure| E(["Check credentials
and fail"])
C -->|Network error| F(["Fallback to
alternative data source"])
Command errors occur when Redis receives an invalid or malformed command. These typically indicate a bug in your code.
Common causes:
Examples:
ResponseError: Invalid command or syntax errorWRONGTYPE Operation against a key holding the wrong kind of valueERR unknown commandWhen to handle: Rarely. These usually indicate programming error and so you should fix the errors in your code rather than attempt to handle them at runtime. However, some cases (like invalid user input) may be worth handling.
Example:
graph TB
A["User provides
JSONPath expression"]
A --> B["Try to execute it"]
direction TB
B -->|ResponseError| C["Log the error"]
C --> D(["Return default value
or error message to user"])
B -->|Success| E(["Use the result"])
Data errors occur when there are problems with the data itself, such as serialization failures, or data corruption.
Common causes:
Examples:
JSONDecodeError: Cannot deserialize JSON dataSerializationError: Cannot serialize objectWhen to handle: Sometimes. If the error is due to user input or external data, handle it gracefully. If it's due to your code, fix the code.
Example:
graph TB
A["Read cached data"]
A --> B["Try to deserialize"]
B -->|Success| C(["Use the data"])
B -->|Deserialization fails| D["Log the error"]
D --> E["Delete corrupted
cache entry"]
E --> F(["Fetch fresh data
from source"])
Resource errors occur when Redis runs out of resources or hits limits.
Common causes:
Examples:
OOM command not allowed when used memory > 'maxmemory'LOADING Redis is loading the dataset in memoryWhen to handle: Sometimes. Some resource errors are temporary (Redis loading), while others indicate a configuration problem.
Example:
graph TB
A{Resource error
occurred?}
A -->|Redis loading| B(["Retry after
a delay"])
A -->|Memory full| C(["Check Redis
configuration and data"])
A -->|Pool exhausted| D(["Increase pool size
or reduce concurrency"])
Use this when the error is unrecoverable or indicates a bug in your code.
When to use:
Example:
try:
result = r.get(key)
except redis.ResponseError as e:
# This indicates a bug in our code
raise # Re-raise the exception
Use this when you have an alternative way to get the data you need, so you can fall back to using the alternative instead of the preferred code.
When to use:
Example:
try:
cached_value = r.get(key)
if cached_value:
return cached_value
except redis.ConnectionError:
logger.warning("Cache unavailable, using database")
# Fallback to database
return database.get(key)
Use this when the error could be due to network load or other temporary conditions.
When to use:
Example:
import time
max_retries = 3
retry_delay = 0.1
for attempt in range(max_retries):
try:
return r.get(key)
except redis.TimeoutError:
if attempt < max_retries - 1:
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
else:
raise
Note that client libraries often implement retry logic for you, so you may just need to provide the right configuration rather than implementing retries yourself. See Client-specific error handling below for links to pages that describe retry configuration for each client library.
Use this when the operation is not critical to your application.
When to use:
Example:
try:
r.setex(key, 3600, value)
except redis.ConnectionError:
logger.warning(f"Failed to cache {key}, continuing without cache")
# Application continues normally
graph LR
Start{Error occurred?}
Start -->|Connection error| C1{Operation type?}
C1 -->|Read| C2["Graceful degradation
fallback"]
C1 -->|Write| C3["Log and continue
or retry"]
C1 -->|Critical| C4["Retry with backoff"]
Start -->|Command error| Cmd1{Error source?}
Cmd1 -->|User input| Cmd2["Log and return
error to user"]
Cmd1 -->|Your code| Cmd3["Fail fast
fix the bug"]
Start -->|Data error| D1{Operation type?}
D1 -->|Read| D2["Log, invalidate,
fallback"]
D1 -->|Write| D3["Log and fail
data is invalid"]
Start -->|Resource error| R1{Error type?}
R1 -->|Redis loading| R2["Retry with backoff"]
R1 -->|Pool exhausted| R3["Increase pool size"]
R1 -->|Memory full| R4["Check configuration"]
In production, you may find it useful to log errors when they occur and monitor the logs for patterns. This can help you identify which errors are most common and whether your retry and fallback strategies are effective. Note that some Redis client libraries have built-in instrumentation that can provide this information for you (see [Observability]({{< relref "/develop/clients/observability" >}}) for a full description).
Example:
logger.error(
"Redis operation failed",
extra={
"error_type": type(e).__name__,
"operation": "get",
"key": key,
"attempt": attempt,
"timestamp": datetime.now().isoformat(),
}
)
These metrics help you identify patterns and potential issues.
Problem: If you catch all exceptions, you might catch unexpected errors and hide bugs.
Example (wrong):
try:
result = r.get(key)
except Exception: # Too broad - some errors indicate code problems.
pass
Better approach: Catch specific exception types.
Example (correct):
try:
result = r.get(key)
except redis.ConnectionError:
# Handle connection error
pass
Problem: Different errors need different handling. For example, retrying a syntax error won't help.
Example (wrong):
try:
result = r.get(key)
except redis.ResponseError:
# Retry? This won't help if it's a syntax error.
retry()
Better approach: Handle each error type differently based on whether or not it is recoverable.
Example (correct):
try:
result = r.get(key)
except redis.TimeoutError:
retry() # Retry on timeout
except redis.ResponseError:
raise # Fail on syntax error
Problem: Connection pool errors indicate a configuration or concurrency issue that needs to be addressed.
Example (wrong):
# Pool is exhausted, but we don't handle it
result = r.get(key) # Might timeout waiting for connection
Better approach: Monitor pool usage and increase size if needed.
For detailed information about exceptions in your client library, see: