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

# Errors & retries

> Exception hierarchy, the built-in retry policy, and how to override it.

Every non-success path through the SDK raises a subclass of `STXException`. The hierarchy is designed so you can catch at whatever specificity you need.

## Exception hierarchy

```
STXException                     — catch-all for anything the SDK raises
├── STXAuthException             — any auth problem
│   ├── STXAuthenticationFailedException   — bad email / password
│   ├── STXTwoFactorRequiredException      — account needs 2FA
│   └── STXTokenExpiredException           — JWT expired between refreshes
├── STXForbiddenException        — 403 — authenticated but not allowed
├── STXValidationException       — 4xx — server rejected your input
│   └── STXInvalidParameterException
├── STXNotFoundException         — 404
├── STXRateLimitException        — 429 — has `.retry_after: float`
├── STXServerException           — 5xx — server-side error
└── STXTransportException        — network, DNS, TLS, connection reset
```

Import from `stx.exceptions`:

```python theme={null}
from stx.exceptions import (
    STXException,
    STXAuthException,
    STXAuthenticationFailedException,
    STXTwoFactorRequiredException,
    STXTokenExpiredException,
    STXForbiddenException,
    STXValidationException,
    STXInvalidParameterException,
    STXNotFoundException,
    STXRateLimitException,
    STXServerException,
    STXTransportException,
)
```

## Catching strategies

<CodeGroup>
  ```python Coarse (catch-all) theme={null}
  from stx.exceptions import STXException

  try:
      client.place_order(params=...)
  except STXException as e:
      log.error("SDK error: %s", e.message)
  ```

  ```python Medium (group by class) theme={null}
  from stx.exceptions import STXAuthException, STXRateLimitException, STXException

  try:
      client.place_order(params=...)
  except STXAuthException:
      re_login()
  except STXRateLimitException as e:
      await asyncio.sleep(e.retry_after)
  except STXException as e:
      log.error("unhandled: %s", e.message)
  ```

  ```python Fine (per-subclass) theme={null}
  from stx.exceptions import (
      STXAuthenticationFailedException,
      STXTwoFactorRequiredException,
      STXInvalidParameterException,
      STXRateLimitException,
  )

  try:
      client.place_order(params=...)
  except STXAuthenticationFailedException:
      alert("creds are wrong")
  except STXTwoFactorRequiredException:
      handle_2fa()
  except STXInvalidParameterException as e:
      log.warning("input rejected: %s", e.message)
  except STXRateLimitException as e:
      backoff(e.retry_after)
  ```
</CodeGroup>

## Error fields

Every `STXException` exposes:

| Field         | Type            | Notes                                              |
| ------------- | --------------- | -------------------------------------------------- |
| `message`     | `str`           | Human-readable summary                             |
| `status_code` | `Optional[int]` | HTTP status when applicable                        |
| `errors`      | `list[dict]`    | Normalized GraphQL / HTTP error records            |
| `request_id`  | `Optional[str]` | Server-side trace ID (include when reporting bugs) |

`STXRateLimitException` additionally exposes:

* `retry_after: float` — seconds the server told you to wait (from `Retry-After` header, or a sane default).

## Automatic retries

Every call through `STX`/`AsyncSTX` is wrapped in a `RetryPolicy`. Defaults:

| Setting              | Default                         |
| -------------------- | ------------------------------- |
| `max_attempts`       | 3                               |
| `initial_backoff`    | 0.25 s                          |
| `max_backoff`        | 10 s                            |
| `jitter`             | Decorrelated jitter (AWS-style) |
| Honors `Retry-After` | Yes                             |

### What gets retried

Automatically retried:

* `STXTransportException` — network blips, connection resets, TLS errors
* `STXRateLimitException` — with the server's `Retry-After`
* `STXServerException` (5xx) — transient server errors

**Never** retried:

* `STXAuthException` and subclasses — won't fix itself on a retry
* `STXForbiddenException` — permission doesn't change on retry
* `STXValidationException` — your input won't change on retry
* `STXNotFoundException` — resource doesn't exist
* `place_order`, `cancel_order`, and other non-idempotent mutations — **excluded by default** to avoid double-submits

### Overriding the policy

Per-client:

```python theme={null}
from stx import STX
from stx._retry import RetryPolicy

client = STX(
    region="ontario", env="production",
    retry=RetryPolicy(
        max_attempts=5,
        initial_backoff=0.5,
        max_backoff=30.0,
        jitter=True,
    ),
)
```

Per-call:

```python theme={null}
from stx._retry import RetryPolicy

client.markets(
    retry=RetryPolicy(max_attempts=10, initial_backoff=0.1),
)
```

Disable entirely:

```python theme={null}
client.place_order(params=..., retry=RetryPolicy.DISABLED)
```

<Tip>
  The SDK already disables retries on non-idempotent mutations (`place_order`, `cancel_order`). You rarely need `RetryPolicy.DISABLED` yourself — the common case is **raising** `max_attempts` for flaky networks, not lowering it.
</Tip>

### Backoff schedule

With defaults (`initial=0.25`, `max=10`), the delays after each failure roughly follow:

```
attempt 1 → wait 0.25–0.5 s → attempt 2 → wait 0.5–1.5 s → attempt 3 → wait 1.5–4 s → …
```

Decorrelated jitter means two clients retrying in parallel won't synchronize their attempts.

## Handling rate limits

`STXRateLimitException` carries `retry_after` — **always use it** instead of guessing:

```python theme={null}
from stx.exceptions import STXRateLimitException

try:
    client.markets()
except STXRateLimitException as e:
    time.sleep(e.retry_after)
    client.markets()
```

Async:

```python theme={null}
import asyncio
from stx.exceptions import STXRateLimitException

try:
    await client.markets()
except STXRateLimitException as e:
    await asyncio.sleep(e.retry_after)
    await client.markets()
```

The built-in retry policy does this for you already — you only need to handle it manually if you've disabled retries or exhausted `max_attempts`.

## Debugging

Every exception includes a `request_id` — quote it when filing a bug:

```python theme={null}
try:
    client.place_order(params=...)
except STXException as e:
    log.error("request_id=%s errors=%s", e.request_id, e.errors)
    raise
```

Enable debug logging to see every retry attempt and backoff delay:

```python theme={null}
import logging
logging.getLogger("stx").setLevel(logging.DEBUG)
```

## Example: robust wrapper

A paranoid wrapper around `place_order` that handles the interesting cases:

```python theme={null}
import asyncio
from stx.exceptions import (
    STXAuthException, STXRateLimitException, STXValidationException, STXException,
)

async def place_order_safely(client, params):
    try:
        return await client.place_order(params=params)
    except STXValidationException as e:
        # Input was bad — don't retry, surface to the caller.
        raise
    except STXAuthException:
        # JWT refresh failed — force re-login on next call.
        from stx.user import User
        User().clear()
        return await client.place_order(params=params)
    except STXRateLimitException as e:
        await asyncio.sleep(e.retry_after)
        return await client.place_order(params=params)
    except STXException as e:
        log.error("place_order failed: request_id=%s", e.request_id)
        raise
```

## Next

<CardGroup cols={2}>
  <Card title="Configuration" icon="sliders" href="/sdks/python/configuration">
    Tune the retry policy, logging, and schema source.
  </Card>

  <Card title="WebSockets" icon="tower-broadcast" href="/sdks/python/websockets">
    WS reconnect uses the same `RetryPolicy` surface.
  </Card>
</CardGroup>
