---
title: "Errors"
description: "Sentinel errors for storage operations: not found, quota exceeded, driver closed, and more."
---

Trove defines sentinel errors for all failure cases. Use `errors.Is()` to match specific error conditions regardless of wrapping.

## Resource Errors

Errors related to missing or duplicate resources:

| Error | Description |
|-------|-------------|
| `ErrNotFound` | A requested resource cannot be found |
| `ErrBucketNotFound` | A bucket cannot be found |
| `ErrBucketExists` | Creating a bucket that already exists |
| `ErrObjectNotFound` | An object cannot be found |

## Validation Errors

Errors related to invalid input:

| Error | Description |
|-------|-------------|
| `ErrKeyEmpty` | Object key is empty (required field) |
| `ErrBucketEmpty` | Bucket name is empty (required field) |
| `ErrInvalidDSN` | DSN string cannot be parsed |

## Driver Errors

Errors related to driver lifecycle:

| Error | Description |
|-------|-------------|
| `ErrDriverClosed` | Operation attempted on a closed driver |
| `ErrNilDriver` | `Open` called with a nil driver |
| `ErrBackendNotFound` | Named backend does not exist in the router |

## Integrity Errors

Errors related to data integrity and security:

| Error | Description |
|-------|-------------|
| `ErrChecksumMismatch` | Checksum verification failed -- data may be corrupted |
| `ErrContentBlocked` | Content rejected by scanning middleware (threat detected) |

## Quota Errors

| Error | Description |
|-------|-------------|
| `ErrQuotaExceeded` | Storage quota exceeded for the tenant or bucket |

## Stream Errors

Errors related to streaming and upload sessions:

| Error | Description |
|-------|-------------|
| `ErrStreamClosed` | Writing to a closed stream |
| `ErrStreamNotActive` | Operation requires an active stream |
| `ErrUploadExpired` | Resumable upload session has expired |
| `ErrPoolClosed` | Stream pool has been shut down |
| `ErrMaxStreamsReached` | Pool concurrency limit reached |

## Matching Errors

Always use `errors.Is()` to check for sentinel errors, since errors may be wrapped with additional context:

```go
import (
    "errors"

    "github.com/xraph/trove"
)

reader, err := t.Get(ctx, "data", "report.csv")
if err != nil {
    switch {
    case errors.Is(err, trove.ErrObjectNotFound):
        // Object does not exist -- return 404
        http.Error(w, "Not found", http.StatusNotFound)

    case errors.Is(err, trove.ErrBucketNotFound):
        // Bucket does not exist -- return 404
        http.Error(w, "Bucket not found", http.StatusNotFound)

    case errors.Is(err, trove.ErrChecksumMismatch):
        // Data corruption detected
        log.Error("integrity failure", "err", err)
        http.Error(w, "Data integrity error", http.StatusInternalServerError)

    case errors.Is(err, trove.ErrDriverClosed):
        // Driver shut down -- service unavailable
        http.Error(w, "Service unavailable", http.StatusServiceUnavailable)

    default:
        http.Error(w, "Internal error", http.StatusInternalServerError)
    }
    return
}
```

## HTTP Status Code Mapping

When Trove is used as an HTTP API, sentinel errors map to standard HTTP status codes:

| Error | HTTP Status | Code |
|-------|-------------|:----:|
| `ErrNotFound` | Not Found | 404 |
| `ErrBucketNotFound` | Not Found | 404 |
| `ErrObjectNotFound` | Not Found | 404 |
| `ErrBucketExists` | Conflict | 409 |
| `ErrKeyEmpty` | Bad Request | 400 |
| `ErrBucketEmpty` | Bad Request | 400 |
| `ErrInvalidDSN` | Bad Request | 400 |
| `ErrDriverClosed` | Service Unavailable | 503 |
| `ErrNilDriver` | Internal Server Error | 500 |
| `ErrBackendNotFound` | Not Found | 404 |
| `ErrChecksumMismatch` | Conflict | 409 |
| `ErrQuotaExceeded` | Insufficient Storage | 507 |
| `ErrContentBlocked` | Unprocessable Entity | 422 |
| `ErrStreamClosed` | Gone | 410 |
| `ErrUploadExpired` | Gone | 410 |
| `ErrPoolClosed` | Service Unavailable | 503 |
| `ErrMaxStreamsReached` | Too Many Requests | 429 |

## Creating Application Errors

Wrap Trove sentinel errors to add context without losing the ability to match:

```go
import "fmt"

func getDocument(ctx context.Context, t *trove.Trove, docID string) error {
    _, err := t.Get(ctx, "documents", docID)
    if err != nil {
        return fmt.Errorf("get document %s: %w", docID, err)
    }
    return nil
}

// Callers can still match the original error:
err := getDocument(ctx, t, "missing.pdf")
if errors.Is(err, trove.ErrObjectNotFound) {
    // Matched despite wrapping
}
```
