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

# Error Handling

> Handle API errors with TheoApiError.

## TheoApiError

All REST API errors are thrown as `TheoApiError` instances with structured details.

```typescript theme={null}
import { Theo, TheoApiError } from "@hitheo/sdk";

try {
  const res = await theo.complete({ prompt: "Hello" });
} catch (err) {
  if (err instanceof TheoApiError) {
    console.log(err.status);              // 401
    console.log(err.message);             // "Theo API error 401: Invalid API key"
    console.log(err.details?.code);       // "invalid_api_key"
    console.log(err.details?.type);       // "authentication_error"
    console.log(err.details?.request_id); // "req_9f2e1a" — share in support tickets
    console.log(err.url);                 // "https://www.hitheo.ai/api/v1/completions"
  }
}
```

## Error Properties

| Property  | Type             | Description                                                                                         |
| --------- | ---------------- | --------------------------------------------------------------------------------------------------- |
| `status`  | `number`         | HTTP status code (`0` for network/timeout/cancel)                                                   |
| `kind`    | `string`         | `"http"` \| `"timeout"` \| `"cancelled"` \| `"network"` — branch on this instead of string-matching |
| `message` | `string`         | Human-readable error message                                                                        |
| `body`    | `string`         | Raw response body                                                                                   |
| `url`     | `string`         | Request URL                                                                                         |
| `details` | `object \| null` | Parsed `{ message, type, code, request_id }`                                                        |

## Error Types

`TheoApiError` is the base class — every error below is `instanceof TheoApiError`, so existing catch blocks keep working. Branch on the subclass (or `err.kind`) for precise handling:

* **`TheoTimeoutError`** (`kind: "timeout"`) — the SDK aborted after `timeoutMs` (unary / stream connect) or `streamIdleTimeoutMs` (stream idle). Carries the offending budget in `.timeoutMs`. **Not** auto-retried on POSTs.
* **`TheoCancelledError`** (`kind: "cancelled"`) — you aborted via `TheoStream.cancel()` or an `AbortSignal` passed to `waitForJob()`.
* **`TheoUsageError`** (extends `Error`, not `TheoApiError`) — thrown synchronously for misuse before any network call, e.g. passing `mode: "research" | "video"` to `complete()` / `stream()` (use the [async job](/guides/async-jobs) methods instead).

```typescript theme={null}
import { TheoApiError, TheoTimeoutError, TheoCancelledError, TheoUsageError } from "@hitheo/sdk";

try {
  await theo.complete({ prompt });
} catch (err) {
  if (err instanceof TheoUsageError) { /* fix the call shape */ }
  else if (err instanceof TheoTimeoutError) { console.error(`timed out after ${err.timeoutMs}ms`); }
  else if (err instanceof TheoCancelledError) { /* user aborted — not an error */ }
  else if (err instanceof TheoApiError && err.kind === "network") { /* offline / retry later */ }
}
```

## Stream Errors

The SSE `error` event carries the **same envelope as the REST error body** — reuse one code path for both.

```typescript theme={null}
const stream = theo.stream({ prompt: "..." });

for await (const event of stream) {
  if (event.type === "error") {
    const { message, type, code, request_id } = event.data.error;
    throw new Error(`[${type}/${code}] ${message} (request_id=${request_id})`);
  }
  // ...
}
```

This is a change from `0.1.x`, where stream errors were `{ message }` only. See the [Streaming Completions](/api-reference/completions/streaming) reference for the full envelope schema.

## First-Run Diagnostic

Instead of surfacing raw errors, `theo.verify()` returns a structured diagnosis with a remediation hint:

```typescript theme={null}
const result = await theo.verify();
if (!result.authenticated) {
  console.error(result.error);  // underlying error
  console.error(result.hint);   // e.g. "Authentication failed. Confirm your API key..."
}
```

Call this as the first line of debugging on a 401. See [401 Troubleshooting](/troubleshooting/401-errors) for the common failure modes.

## Automatic Retries

The SDK retries automatically on:

* **429** — Rate limited (respects `Retry-After` header up to 60s)
* **5xx** — Server errors (exponential backoff: 1s, 2s, 4s, max 8s)
  Client errors (400, 401, 403, 404) are **not** retried. **Timeouts are not retried on non-idempotent POSTs** (`complete`, `research`, `video`) — re-issuing could double-run and double-bill the request; pair your own retry with an `Idempotency-Key` if you need one. See [Timeouts & Retries](/sdk-reference/timeouts-and-retries) for the full model, including the stream **idle** timeout.
  Configure:

```typescript theme={null}
const theo = new Theo({
  apiKey: "...",
  maxRetries: 3,               // Default: 2
  timeoutMs: 60000,            // Default: 30000 (unary total; stream connect)
  streamIdleTimeoutMs: 180000, // Default: 120000 (between-chunks)
});
```

## Cancellation

`TheoStream.cancel()` aborts an in-flight stream without throwing. The underlying `fetch` is aborted via `AbortController`; subsequent events stop arriving and the iterator ends cleanly.

```typescript theme={null}
const stream = theo.stream({ prompt: "..." });
setTimeout(() => stream.cancel(), 3000); // Graceful "Stop generating"
for await (const event of stream) { /* ... */ }
```
