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

# Error Handling

> Exception hierarchy and error handling patterns for the Chatbase Android SDK.

## Exception Hierarchy

All SDK errors extend `ChatbaseException`:

```
ChatbaseException
├── ApiException        — API errors from the Chatbase server
└── NetworkException    — Connection failures, DNS errors, timeouts
```

## ApiException

`class ApiException : ChatbaseException` — **Package:** `com.chatbase.sdk.exception`

Thrown when the Chatbase API returns an error response.

```kotlin theme={null}
class ApiException(
    val httpStatus: Int,
    val errorCode: String,
    val errorMessage: String,
    val details: Map<String, String>? = null
) : ChatbaseException
```

<ResponseField name="httpStatus" type="Int" required>
  The HTTP status code of the error response (e.g. `401`, `403`, `429`).
</ResponseField>

<ResponseField name="errorCode" type="String" required>
  Machine-readable error code. Use this for programmatic handling.
</ResponseField>

<ResponseField name="errorMessage" type="String" required>
  Human-readable error description.
</ResponseField>

<ResponseField name="details" type="Map<String, String>?">
  Optional field-level validation errors.
</ResponseField>

### Convenience Properties

```kotlin theme={null}
val isRateLimited: Boolean      // httpStatus == 429
val isNotFound: Boolean         // httpStatus == 404
val isCreditsExhausted: Boolean // httpStatus == 402
```

<Tip>
  For statuses without a helper — such as `401` (authentication) or `403` (access denied) — check `httpStatus` or `errorCode` directly.
</Tip>

## Error Codes

These are the error codes you may encounter through `ApiException.errorCode`:

<table>
  <thead>
    <tr>
      <th>Status</th>
      <th style={{ whiteSpace: 'nowrap' }}>Code</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr><td>400</td><td style={{ whiteSpace: 'nowrap' }}><code>VALIDATION\_INVALID\_BODY</code></td><td>The request body failed validation. <code>details</code> maps field names to messages. Also returned when a tool result exceeds the 20 KB limit.</td></tr>
    <tr><td>400</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_RETRY\_NO\_USER\_MESSAGE</code></td><td>The message passed to <code>retry()</code> has no preceding user message to retry from.</td></tr>
    <tr><td>401</td><td style={{ whiteSpace: 'nowrap' }}><code>AUTH\_INVALID\_JWT</code></td><td>The JWT token passed to <code>identify()</code> is invalid, expired, or could not be verified — including when identity verification is not configured for the agent. See <a href="/docs/android-sdk/user-identity">User Identity</a>.</td></tr>
    <tr><td>402</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_CREDITS\_EXHAUSTED</code></td><td>The workspace's message credit balance is zero. Upgrade the plan or wait for credits to reset. Caught by <code>isCreditsExhausted</code>.</td></tr>
    <tr><td>402</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_AGENT\_CREDITS\_EXHAUSTED</code></td><td>The specific agent's credit allocation has been used up. Caught by <code>isCreditsExhausted</code>.</td></tr>
    <tr><td>403</td><td style={{ whiteSpace: 'nowrap' }}><code>AUTH\_OWNERSHIP\_MISMATCH</code></td><td>The conversation belongs to a different user or device. Also returned by <code>retry()</code> and <code>listMessages()</code> when the conversation does not exist.</td></tr>
    <tr><td>403</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_CONVERSATION\_MISMATCH</code></td><td>The conversation could not be resolved for this agent.</td></tr>
    <tr><td>403</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_MODEL\_NOT\_ALLOWED</code></td><td>The agent uses a model not available on the current plan.</td></tr>
    <tr><td>403</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_CONVERSATION\_NOT\_ONGOING</code></td><td>The conversation has ended or was taken over and cannot receive new messages. Start a new conversation.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>AGENT\_NOT\_FOUND</code></td><td>No agent matches the provided ID, or the <strong>Android SDK channel is not enabled</strong> for the agent (see <a href="/docs/android-sdk/overview#quick-start">Quick Start</a>). Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>RESOURCE\_NOT\_FOUND</code></td><td>The conversation or message does not exist. Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_RETRY\_MESSAGE\_NOT\_FOUND</code></td><td>The message ID provided for <code>retry()</code> was not found. Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>RESOURCE\_TOOL\_CALL\_NOT\_FOUND</code></td><td>The tool call was not found or has expired. Can surface through the automatic tool loop. Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>RESOURCE\_TOOL\_CALL\_MISMATCH</code></td><td>The tool call does not belong to this conversation. Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>404</td><td style={{ whiteSpace: 'nowrap' }}><code>RESOURCE\_TOOL\_RESULT\_NOT\_PENDING</code></td><td>No pending tool result exists for this tool call — usually a duplicate submission. Caught by <code>isNotFound</code>.</td></tr>
    <tr><td>429</td><td style={{ whiteSpace: 'nowrap' }}><code>RATE\_LIMIT\_TOO\_MANY\_REQUESTS</code></td><td>Rate limit exceeded (1,000 requests per 10 seconds per device). Back off and retry. Caught by <code>isRateLimited</code>.</td></tr>
    <tr><td>500</td><td style={{ whiteSpace: 'nowrap' }}><code>CHAT\_STREAMING\_ERROR</code></td><td>The response stream failed server-side. Safe to retry.</td></tr>
    <tr><td>500</td><td style={{ whiteSpace: 'nowrap' }}><code>INTERNAL\_SERVER\_ERROR</code></td><td>An unexpected server error occurred. Retry, or contact support if it persists.</td></tr>
  </tbody>
</table>

## NetworkException

`class NetworkException : ChatbaseException` — **Package:** `com.chatbase.sdk.exception`

```kotlin theme={null}
class NetworkException(
    message: String,
    cause: Throwable? = null
) : ChatbaseException
```

Thrown for connection-level failures — DNS errors, socket timeouts, no internet connectivity, and similar issues.

## Handling Errors

<Tabs>
  <Tab title="Suspend Functions" icon="code">
    Use a try-catch block with the SDK's exception hierarchy:

    ```kotlin theme={null}
    try {
        val response = client.sendMessage("Hello")
    } catch (e: ApiException) {
        when {
            e.httpStatus == 401 -> {
                println("Authentication failed. Check your JWT token.")
            }
            e.httpStatus == 403 -> {
                println("Access denied. Check conversation ownership or plan.")
            }
            e.isRateLimited -> {
                println("Rate limited. Back off and retry.")
            }
            e.isCreditsExhausted -> {
                println("No credits remaining. Upgrade plan.")
            }
            e.isNotFound -> {
                println("Not found. Check your agent ID.")
            }
            else -> {
                println("API error: ${e.errorCode} — ${e.errorMessage}")
            }
        }
    } catch (e: NetworkException) {
        println("Network error: ${e.message}")
    } catch (e: ChatbaseException) {
        println("Unknown SDK error: ${e.message}")
    }
    ```
  </Tab>

  <Tab title="Streaming Callbacks" icon="wave-pulse">
    Handle errors via the `onError` callback:

    ```kotlin theme={null}
    client.sendMessage("Hello") {
        onTextDelta { delta -> print(delta) }
        onError { error ->
            when (error) {
                is ApiException -> println("API error: ${error.errorCode}")
                is NetworkException -> println("Network error: ${error.message}")
                else -> println("Error: ${error.message}")
            }
        }
    }
    ```
  </Tab>

  <Tab title="Flow" icon="bars-staggered">
    Check for `ChatStreamEvent.Error` events:

    ```kotlin theme={null}
    client.sendMessageStream("Hello").collect { event ->
        when (event) {
            is ChatStreamEvent.TextDelta -> print(event.delta)
            is ChatStreamEvent.Error -> {
                when (val ex = event.exception) {
                    is ApiException -> println("${ex.errorCode}: ${ex.errorMessage}")
                    is NetworkException -> println("Network: ${ex.message}")
                    else -> println("Error: ${ex.message}")
                }
            }
            else -> {}
        }
    }
    ```
  </Tab>
</Tabs>

## Related

<CardGroup cols={2}>
  <Card title="Streaming" icon="wave-pulse" href="/docs/android-sdk/streaming">
    Streaming callbacks and error events
  </Card>

  <Card title="Overview" icon="book-open" href="/docs/android-sdk/overview">
    SDK setup and configuration
  </Card>
</CardGroup>
