# Error Handling & Reconnection

## HTTP errors (during handshake)

If authentication or rate limiting fails, the server rejects the WebSocket upgrade with an HTTP error:

| HTTP Code | Reason                                  | Action                              |
| --------- | --------------------------------------- | ----------------------------------- |
| `400`     | Malformed request                       | Fix your WebSocket client           |
| `401`     | Missing API key                         | Add `X-API-Key` header              |
| `403`     | Invalid, revoked, or expired key        | Contact administrator for a new key |
| `429`     | Rate limit or connection limit exceeded | Wait and retry with backoff         |

## WebSocket close codes

Once connected, the server may close your connection with a close frame. The reason field tells you why:

| Code   | Reason                | Meaning                                            | Should reconnect?                |
| ------ | --------------------- | -------------------------------------------------- | -------------------------------- |
| `1000` | `key_expired`         | Your API key's expiration date has passed          | No -- get a new key              |
| `1000` | `key_revoked`         | Administrator revoked your key                     | No -- get a new key              |
| `1008` | `too_slow`            | You fell 10+ messages behind (lagging consumer)    | Yes                              |
| `1008` | `rate_limit_exceeded` | You sent too many messages to the server (>30/min) | Yes -- stop sending messages     |
| `1009` | `frame_too_large`     | You sent a frame larger than 1 KB                  | Yes -- stop sending large frames |

## Recommended reconnection strategy

### Exponential backoff

```
Attempt 1: wait 1 second
Attempt 2: wait 2 seconds
Attempt 3: wait 4 seconds
Attempt 4: wait 8 seconds
...
Cap: 300 seconds (5 minutes)
```

### Decision logic

```
On disconnect:
  |
  |-- Close reason = "key_expired" or "key_revoked"?
  |     → Stop. Your key is no longer valid.
  |
  |-- Close reason = "rate_limit_exceeded"?
  |     → Wait 60s, then reconnect. Stop sending messages.
  |
  |-- Close reason = "too_slow"?
  |     → Reconnect immediately. Process messages faster.
  |
  |-- HTTP 429?
  |     → Back off. You're connecting too frequently.
  |
  |-- Unexpected disconnect / network error?
        → Reconnect with exponential backoff.
```

### Heartbeat-based health check

Monitor heartbeats to detect silent disconnections:

```python
import asyncio

HEARTBEAT_TIMEOUT = 35  # seconds

async def monitor_connection(ws):
    while True:
        try:
            msg = await asyncio.wait_for(ws.recv(), timeout=HEARTBEAT_TIMEOUT)
            data = json.loads(msg)
            if data["type"] == "announcement":
                handle_announcement(data)
        except asyncio.TimeoutError:
            print("No heartbeat received -- reconnecting")
            break  # Exit loop and reconnect
```

{% hint style="info" %}
The server sends heartbeats every 30 seconds. If you receive nothing for 35 seconds, assume the connection is dead and reconnect.
{% endhint %}
