> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Error type hierarchy for the Go SDK, how to match specific errors with errors.As, and retry patterns for transient failures.

```go theme={null}
import (
    "errors"
    "github.com/declaw-ai/declaw-go"
)
```

All SDK errors embed `*SandboxError`, which implements the `error` interface. Use `errors.As` to match specific error types, or check the base `*SandboxError` to catch any Declaw error.

## Error hierarchy

```
error
└── *SandboxError
    ├── *TimeoutError
    ├── *NotFoundError
    ├── *AuthenticationError
    ├── *InvalidArgumentError
    ├── *NotEnoughSpaceError
    ├── *FileUploadError
    ├── *GitAuthError
    ├── *GitUpstreamError
    ├── *CommandExitError
    ├── *InsufficientBalanceError
    ├── *RateLimitError
    ├── *TemplateError
    └── *BuildError
```

***

## `SandboxError`

Base error type for all Declaw errors.

```go theme={null}
type SandboxError struct {
    Message    string
    SandboxID  string
    StatusCode int
}
```

| Field        | Type     | Description                             |
| ------------ | -------- | --------------------------------------- |
| `Message`    | `string` | Human-readable error description        |
| `SandboxID`  | `string` | The sandbox ID involved, when available |
| `StatusCode` | `int`    | HTTP status code from the API response  |

***

## `NotFoundError`

Returned when a sandbox or resource does not exist (HTTP 404).

```go theme={null}
sbx, err := declaw.Connect(ctx, "nonexistent-id")
if err != nil {
    var nfe *declaw.NotFoundError
    if errors.As(err, &nfe) {
        fmt.Println("Sandbox not found:", nfe.Message)
    }
}
```

***

## `AuthenticationError`

Returned when the API key is missing or invalid (HTTP 401/403).

```go theme={null}
sbx, err := declaw.Create(ctx)
if err != nil {
    var ae *declaw.AuthenticationError
    if errors.As(err, &ae) {
        fmt.Println("Invalid API key")
    }
}
```

***

## `InsufficientBalanceError`

Returned when the account has insufficient balance (HTTP 402).

```go theme={null}
sbx, err := declaw.Create(ctx)
if err != nil {
    var ibe *declaw.InsufficientBalanceError
    if errors.As(err, &ibe) {
        fmt.Println("Top up required:", ibe.Message)
    }
}
```

***

## `RateLimitError`

Returned when the API rate limit is exceeded (HTTP 429). Inspect `RetryAfter` and back off before retrying.

```go theme={null}
sbx, err := declaw.Create(ctx)
if err != nil {
    var rle *declaw.RateLimitError
    if errors.As(err, &rle) {
        fmt.Printf("Rate limited. Retry after %v\n", rle.RetryAfter)
        time.Sleep(rle.RetryAfter)
    }
}
```

| Field        | Type            | Description                          |
| ------------ | --------------- | ------------------------------------ |
| `RetryAfter` | `time.Duration` | How long to wait before retrying     |
| `Limit`      | `int`           | Rate limit ceiling                   |
| `Remaining`  | `int`           | Requests remaining in current window |

***

## `CommandExitError`

Returned when a command exits with a non-zero exit code. Contains the full stdout, stderr, and exit code. Note that `Run()` returns **both** the `*CommandResult` and the error, so you can inspect output even on failure.

```go theme={null}
result, err := sbx.Commands.Run(ctx, "python3 -c \"raise ValueError('oops')\"")
if err != nil {
    var exitErr *declaw.CommandExitError
    if errors.As(err, &exitErr) {
        fmt.Printf("Exit code: %d\n", exitErr.ExitCode)
        fmt.Printf("Stderr: %s\n", exitErr.Stderr)
    }
}
```

| Field      | Type     | Description              |
| ---------- | -------- | ------------------------ |
| `ExitCode` | `int`    | The process exit code    |
| `Stdout`   | `string` | Captured standard output |
| `Stderr`   | `string` | Captured standard error  |

***

## `TimeoutError`

Returned when an operation exceeds its configured timeout (HTTP 408).

***

## `InvalidArgumentError`

Returned when invalid arguments are passed to an API call (HTTP 422).

***

## `NotEnoughSpaceError`

Returned when the sandbox filesystem is full (HTTP 507).

***

## `TemplateError` / `BuildError`

Returned on template build or retrieval errors.

***

## Catching all Declaw errors

```go theme={null}
sbx, err := declaw.Create(ctx)
if err != nil {
    var se *declaw.SandboxError
    if errors.As(err, &se) {
        fmt.Printf("Declaw error (HTTP %d): %s\n", se.StatusCode, se.Message)
        if se.SandboxID != "" {
            fmt.Println("Sandbox ID:", se.SandboxID)
        }
    }
}
```

***

## Retry patterns

### Simple retry with backoff

```go theme={null}
func runWithRetry(ctx context.Context, sbx *declaw.Sandbox, cmd string, retries int) (string, error) {
    delay := time.Second
    for attempt := 0; attempt < retries; attempt++ {
        result, err := sbx.Commands.Run(ctx, cmd,
            declaw.WithRunTimeout(30*time.Second),
        )
        if err == nil {
            return result.Stdout, nil
        }
        // Don't retry deterministic failures
        var exitErr *declaw.CommandExitError
        if errors.As(err, &exitErr) {
            return "", err // non-zero exit won't improve on retry
        }
        var ae *declaw.AuthenticationError
        if errors.As(err, &ae) {
            return "", err
        }
        var nfe *declaw.NotFoundError
        if errors.As(err, &nfe) {
            return "", err
        }
        if attempt < retries-1 {
            time.Sleep(delay)
            delay *= 2
        }
    }
    return "", fmt.Errorf("command failed after %d retries", retries)
}
```

### Handle rate limits

```go theme={null}
func createWithRateLimit(ctx context.Context) (*declaw.Sandbox, error) {
    for {
        sbx, err := declaw.Create(ctx, declaw.WithTimeout(300))
        if err == nil {
            return sbx, nil
        }
        var rle *declaw.RateLimitError
        if errors.As(err, &rle) && rle.RetryAfter > 0 {
            time.Sleep(rle.RetryAfter)
            continue
        }
        return nil, err
    }
}
```

### Handle non-zero exit codes

```go theme={null}
result, err := sbx.Commands.Run(ctx, "python3 risky_script.py")
if err != nil {
    var exitErr *declaw.CommandExitError
    if errors.As(err, &exitErr) {
        fmt.Printf("Script failed (exit %d)\n", exitErr.ExitCode)
        fmt.Printf("Stderr: %s\n", exitErr.Stderr)
        // result is still available
        fmt.Printf("Stdout: %s\n", result.Stdout)
    } else {
        log.Fatal(err) // network or other error
    }
}
```
