---
summary: HTTP error shape and SDK exceptions.
title: Errors
path: reference/errors
status: published
---

# Errors

## REST error shape

Errors return a 4xx/5xx status and a JSON body:

```json
{
  "error": {
    "code": "ATLAS_001",
    "message": "Human-readable message",
    "field": "name"
  }
}
```

`field` is present when a specific input caused the error. Validation failures (`422`)
carry a `details` array under `error` describing each offending field.

### Common status codes

| Status | Meaning | Typical cause |
|---|---|---|
| `400` | Bad request | Wrong state (e.g. version not `uploading`), invalid argument. |
| `401` | Unauthenticated | Missing/invalid token. Sends `WWW-Authenticate: Bearer`. |
| `403` | Forbidden | Authenticated but lacking the required permission. |
| `404` | Not found | Resource missing — or hidden by permissions (restricted resources 404 rather than 403). |
| `409` | Conflict | Duplicate name/version, or deleting a non-empty namespace. |
| `416` | Range not satisfiable | Bad `Range` header on a file download. |
| `422` | Validation error | Body failed schema validation. |
| `429` | Rate limited | Too many requests; check `retry_after`. |
| `503` | Unavailable | P2P endpoints when P2P is disabled. |

## SDK exceptions

The SDK raises typed exceptions from `scaiatlas.exceptions` (the common ones are also
importable from `scaiatlas`). Catch by class:

```
ScaiAtlasError                      base of everything
├── APIError                        any HTTP error — .status_code, .response_body
│   ├── AuthenticationError   401
│   ├── AuthorizationError    403
│   ├── NotFoundError         404
│   ├── ConflictError         409
│   ├── ValidationError       422   .errors -> list of field errors
│   └── RateLimitError        429   .retry_after -> seconds (or None)
├── UploadError                     .file_path, .chunk_index
├── DownloadError                   .file_path, .bytes_downloaded
│   └── ChecksumError               .expected, .actual
└── TimeoutError                    .timeout_seconds
```

### Handling patterns

```python
from scaiatlas import ScaiAtlas
from scaiatlas.exceptions import (
    NotFoundError, AuthenticationError, ConflictError,
    RateLimitError, ValidationError, ChecksumError,
)

client = ScaiAtlas.from_env()

try:
    model = client.models.get("poolnoodle", "nonexistent")
except NotFoundError:
    ...                                  # 404
except AuthenticationError:
    ...                                  # 401 — bad/expired credential
except RateLimitError as e:
    wait = e.retry_after or 5            # back off and retry
except ValidationError as e:
    for err in e.errors:                 # per-field detail
        print(err)
```

```python
# Idempotent create
try:
    client.models.create("poolnoodle", "noodletron", type="llm")
except ConflictError:
    pass                                 # already exists — fine
```

```python
# Integrity failure on download
from scaiatlas.exceptions import ChecksumError
try:
    client.download.version("poolnoodle", "noodletron", "1.0.0", destination="./x")
except ChecksumError as e:
    print(f"checksum mismatch: expected {e.expected}, got {e.actual}")
```

### Retries

Transient statuses (`408`, `429`, `500`, `502`, `503`, `504`) are retried
automatically with exponential backoff. Tune via `RetryConfig`
(from `scaiatlas._http.retry`) passed as `retry_config=` (or through `ClientConfig`).
