# Error handling

```json metadata
{
  "title": "Error handling",
  "description": "Learn how to handle errors when using redis-rb.",
  "categories": null,
  "topics": ["error-handling","resilience"],
  "relatedPages": ["/develop/clients/error-handling"],
  "scope": "implementation",
  "tableOfContents": {"sections":[{"children":[{"id":"key-exceptions","title":"Key exceptions"}],"id":"exception-hierarchy","title":"Exception hierarchy"},{"children":[{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue"}],"id":"applying-error-handling-patterns","title":"Applying error handling patterns"},{"id":"transaction-conflicts","title":"Transaction conflicts"},{"id":"see-also","title":"See also"}]}

,
  "codeExamples": []
}
```
`redis-rb` uses **exceptions** to signal errors. Most documentation examples
focus on the "happy path", but production code should handle connection and
command failures explicitly. This page explains how error handling works in
`redis-rb` and how to apply common error handling patterns.

For an overview of error types and handling strategies, see
[Error handling](https://redis.io/docs/latest/develop/clients/error-handling).

## Exception hierarchy

`redis-rb` organizes exceptions under `Redis::BaseError`:

```hierarchy {type="exception"}
"Redis::BaseError":
    _meta:
        description: "Base class for redis-rb exceptions"
    "Redis::ProtocolError":
    "Redis::CommandError":
        "Redis::PermissionError":
        "Redis::WrongTypeError":
        "Redis::OutOfMemoryError":
        "Redis::NoScriptError":
    "Redis::BaseConnectionError":
        "Redis::CannotConnectError":
        "Redis::ConnectionError":
        "Redis::TimeoutError":
        "Redis::ReadOnlyError":
    "...":
        _meta:
            ellipsis: true
            description: "Other redis-rb exception types"
```

### Key exceptions

The following exceptions are the most commonly encountered in `redis-rb`
applications. See
[Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)
for a more detailed discussion of these errors and their causes.

| Exception | When it occurs | Recoverable | Recommended action |
|---|---|---|---|
| `Redis::CannotConnectError` | A connection could not be established | ✅ | Retry with backoff or fall back |
| `Redis::TimeoutError` | A read or blocking operation timed out | ✅ | Retry with backoff and review timeouts |
| `Redis::CommandError` | Redis returned an error reply such as `ERR` or `WRONGTYPE` | ❌ | Fix the command, arguments, or data model |
| `Redis::ConnectionError` | An established connection was lost | ✅ | Reconnect and retry cautiously |

## Applying error handling patterns

The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview
describes four main patterns. The sections below show how to implement them in
`redis-rb`:

### Pattern 1: Fail fast

Catch specific exceptions that represent unrecoverable errors and re-raise them
(see
[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)
for a full description):

```ruby
begin
  redis.get(key)
rescue Redis::CommandError
  # This indicates a bug in our code or schema.
  raise
end
```

### Pattern 2: Graceful degradation

Catch connection failures and fall back to an alternative (see
[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)
for a full description):

```ruby
begin
  cached_value = redis.get(key)
  return cached_value unless cached_value.nil?
rescue Redis::CannotConnectError, Redis::ConnectionError
  logger.warn("Cache unavailable, using database")
end

database.get(key)
```

### Pattern 3: Retry with backoff

Retry on temporary errors such as timeouts (see
[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)
for a full description):

```ruby
delay = 0.1

3.times do |attempt|
  begin
    return redis.get(key)
  rescue Redis::CannotConnectError, Redis::ConnectionError, Redis::TimeoutError => e
    raise e if attempt == 2

    sleep(delay)
    delay *= 2
  end
end
```

### Pattern 4: Log and continue

Log non-critical failures and continue (see
[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)
for a full description):

```ruby
begin
  redis.setex(key, 3600, value)
rescue Redis::CannotConnectError, Redis::ConnectionError, Redis::TimeoutError
  logger.warn("Failed to cache #{key}, continuing without cache")
end
```

## Transaction conflicts

When a watched transaction in `redis-rb` loses an optimistic-locking race,
`multi()` returns `nil` instead of raising an exception. Treat that as a
retryable conflict:

```ruby
result = redis.watch(key) do |client|
  current = client.get(key)

  client.multi do |tx|
    tx.set(key, current.upcase)
  end
end

retry_transaction if result.nil?
```

## See also

- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)
- [Pipelines and transactions](https://redis.io/docs/latest/develop/clients/ruby/transpipe)

