# Error Handling

## Error Categories

### Authentication Errors

| Error             | Description              | Action                         |
| ----------------- | ------------------------ | ------------------------------ |
| `UNAUTHENTICATED` | Token missing or invalid | Check token is set in metadata |
| `TOKEN_EXPIRED`   | Token has expired        | Obtain new token               |
| `INVALID_TOKEN`   | Token format incorrect   | Verify token format            |

### Subscription Limit Errors

| Error                                    | Description                   | Limit           |
| ---------------------------------------- | ----------------------------- | --------------- |
| `SUBSCRIPTION_LIMIT_REACHED`             | Total subscriptions exceeded  | 6 total         |
| `TRANSACTION_SUBSCRIPTION_LIMIT_REACHED` | Transaction streams exceeded  | 2 max           |
| `ACCOUNT_SUBSCRIPTION_LIMIT_REACHED`     | Account streams exceeded      | 5 max           |
| `SLOT_SUBSCRIPTION_LIMIT_REACHED`        | Slot streams exceeded         | 2 max           |
| `WALLET_SUBSCRIPTION_LIMIT_REACHED`      | Wallet streams exceeded       | 10 max          |
| `TOO_MANY_WALLET_ADDRESSES`              | Addresses in request exceeded | 10 per request  |
| `TOO_MANY_ACCOUNT_ADDRESSES`             | Accounts in request exceeded  | 100 per request |

### Connection Errors

| Error                | Description                    | Action                       |
| -------------------- | ------------------------------ | ---------------------------- |
| `CONNECTION_ERROR`   | Failed to establish connection | Check network/server address |
| `CONNECTION_CLOSED`  | Server closed connection       | Reconnect with backoff       |
| `CONNECTION_TIMEOUT` | Connection timed out           | Retry with backoff           |
| `STREAM_CLOSED`      | Stream was terminated          | Reconnect                    |

### Request Errors

| Error                     | Description                | Action                   |
| ------------------------- | -------------------------- | ------------------------ |
| `INVALID_REQUEST`         | Malformed request          | Check request format     |
| `INVALID_WALLET_ADDRESS`  | Bad wallet address format  | Use valid Base58 address |
| `INVALID_ACCOUNT_ADDRESS` | Bad account address format | Use valid Base58 address |
| `EMPTY_WALLET_LIST`       | No wallets provided        | Add at least one wallet  |
| `EMPTY_ACCOUNT_LIST`      | No accounts provided       | Add at least one account |

### Server Errors

| Error                | Description                | Action             |
| -------------------- | -------------------------- | ------------------ |
| `INTERNAL`           | Internal server error      | Contact support    |
| `UNAVAILABLE`        | Service unavailable        | Retry with backoff |
| `RESOURCE_EXHAUSTED` | Server resources exhausted | Retry later        |

## Recovery Strategies

### Reconnection with Exponential Backoff

{% code title="reconnect.go" %}

```go
// Go example
func reconnectWithBackoff(ctx context.Context, maxRetries int) error {
    backoff := time.Second
    for i := 0; i < maxRetries; i++ {
        stream, err := client.SubscribeToTransactions(ctx)
        if err == nil {
            return processStream(stream)
        }

        log.Printf("Connection failed, retrying in %v", backoff)
        time.Sleep(backoff)
        backoff *= 2
        if backoff > 30*time.Second {
            backoff = 30 * time.Second
        }
    }
    return errors.New("max retries exceeded")
}
```

{% endcode %}

{% code title="reconnect.rs" %}

```rust
// Rust example
async fn reconnect_with_backoff(max_retries: u32) -> Result<(), Box<dyn Error>> {
    let mut backoff = Duration::from_secs(1);

    for _ in 0..max_retries {
        match client.subscribe_to_transactions().await {
            Ok(stream) => return process_stream(stream).await,
            Err(e) => {
                println!("Connection failed, retrying in {:?}", backoff);
                tokio::time::sleep(backoff).await;
                backoff = std::cmp::min(backoff * 2, Duration::from_secs(30));
            }
        }
    }
    Err("max retries exceeded".into())
}
```

{% endcode %}

{% code title="reconnect.ts" %}

```typescript
// TypeScript example
async function reconnectWithBackoff(maxRetries: number): Promise<void> {
    let backoff = 1000;

    for (let i = 0; i < maxRetries; i++) {
        try {
            await subscribeToTransactions();
            return;
        } catch (err) {
            console.log(`Connection failed, retrying in ${backoff}ms`);
            await new Promise(r => setTimeout(r, backoff));
            backoff = Math.min(backoff * 2, 30000);
        }
    }
    throw new Error('max retries exceeded');
}
```

{% endcode %}

### Error Type Classification

{% code title="error\_classification.go" %}

```go
// Go
func handleError(err error) {
    switch {
    case thorclient.IsStreamDone(err):
        // Normal closure, reconnect
        reconnect()
    case isAuthError(err):
        // Authentication issue, refresh token
        refreshToken()
    case isRateLimitError(err):
        // Too many subscriptions, close unused
        closeUnusedSubscriptions()
    default:
        // Unknown error, log and retry
        log.Printf("Unknown error: %v", err)
        reconnectWithBackoff()
    }
}
```

{% endcode %}

## Monitoring Recommendations

{% stepper %}
{% step %}
**Track error frequencies**

Alert on unusual patterns.
{% endstep %}

{% step %}
**Monitor reconnection rates**

High rates indicate infrastructure issues.
{% endstep %}

{% step %}
**Log error contexts**

Include timestamps and request details.
{% endstep %}

{% step %}
**Set up health checks**

Verify stream connectivity periodically.
{% endstep %}
{% endstepper %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thornode.io/infrastructure/thorstreamer-grpc/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
