> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.twelvelabs.io/v1.3/agents/troubleshooting/error-handling/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.twelvelabs.io/_mcp/server. # Error handling > Handle errors across the Jockey API with typed exceptions, retries, and status polling. This page shows you how to handle errors across the [Jockey](/v1.3/agents/concepts/jockey) API: catch typed exceptions, retry transient failures, and poll for asynchronous status. # Error response format When a request fails, the SDK raises a typed exception: `ApiError` in Python and `TwelvelabsApiError` in Node.js. The exception includes the HTTP status code and the parsed error body, which follows the same shape across the API: ```json { "code": "parameter_not_provided", "message": "The input parameter is required but was not provided." } ``` Catch the exception and inspect its status code and body: **`Python`** ```python Python maxlines=15 from twelvelabs import TwelveLabs from twelvelabs.core.api_error import ApiError client = TwelveLabs(api_key="") try: store = client.knowledge_stores.retrieve(knowledge_store_id="") except ApiError as exc: print(f"Status: {exc.status_code}") print(f"Body: {exc.body}") ``` **`Node.js`** ```javascript Node.js maxlines=15 import { TwelveLabs, TwelvelabsApiError } from "twelvelabs-js"; const client = new TwelveLabs({ apiKey: "" }); try { const store = await client.knowledgeStores.retrieve(""); } catch (err) { if (err instanceof TwelvelabsApiError) { console.log(`Status: ${err.statusCode}`); console.log(`Body: ${JSON.stringify(err.body)}`); } } ``` # HTTP status codes The status code is available on the exception (`status_code` in Python, `statusCode` in Node.js). | Code | Meaning | Action | | ----- | ------------ | ----------------------------------------------- | | `200` | Success | Process response | | `201` | Created | Resource created successfully | | `202` | Accepted | Async operation started (knowledge store items) | | `400` | Bad Request | Fix request parameters | | `401` | Unauthorized | Check API key | | `403` | Forbidden | Check permissions | | `404` | Not Found | Check the resource identifier | | `429` | Rate Limited | Back off and retry | | `500` | Server Error | Retry with backoff | # Retry strategy Retry on transient errors (`429` rate limits and `5xx` server errors). Do not retry client errors (`400`, `401`, `403`, `404`). Fix the request instead. **`Python`** ```python Python maxlines=28 import time from twelvelabs import TwelveLabs from twelvelabs.core.api_error import ApiError client = TwelveLabs(api_key="") def with_retry(call, max_retries=3, base_delay=1): """Call an SDK method with exponential backoff on 429 and 5xx errors.""" for attempt in range(max_retries): try: return call() except ApiError as exc: status = exc.status_code if status == 429 or (status is not None and status >= 500): delay = base_delay * (2 ** attempt) print(f"Received {status}, retrying in {delay}s...") time.sleep(delay) continue raise # Client errors are not retryable raise RuntimeError("Exhausted retries") response = with_retry(lambda: client.responses.create( knowledge_store_id="", input=[{"type": "message", "role": "user", "content": ""}], )) ``` **`Node.js`** ```javascript Node.js maxlines=28 import { TwelveLabs, TwelvelabsApiError } from "twelvelabs-js"; const client = new TwelveLabs({ apiKey: "" }); async function withRetry(call, maxRetries = 3, baseDelay = 1000) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await call(); } catch (err) { if (err instanceof TwelvelabsApiError && (err.statusCode === 429 || err.statusCode >= 500)) { const delay = baseDelay * 2 ** attempt; console.log(`Received ${err.statusCode}, retrying in ${delay / 1000}s...`); await new Promise((r) => setTimeout(r, delay)); continue; } throw err; // Client errors are not retryable } } throw new Error("Exhausted retries"); } const response = await withRetry(() => client.responses.create({ knowledgeStoreId: "", input: [{ type: "message", role: "user", content: "" }], }) ); ``` # Common errors by endpoint ## Assets | Error | Cause | Fix | | ----------------- | --------------------- | ----------------------------------------- | | `method` required | Missing upload method | Add `method: "direct"` or `method: "url"` | | File too large | Direct upload > 200MB | Use URL upload method instead | | Invalid URL | URL not accessible | Check URL is public and reachable | ## Knowledge stores | Error | Cause | Fix | | -------------- | ------------------------------------------ | ------------------------------------------ | | Invalid schema | Bad JSON Schema in ingestion configuration | Validate against JSON Schema draft 2020-12 | | Name required | Missing `name` field | Add a name | ## Knowledge store items | Error | Cause | Fix | | --------------- | ---------------------------- | ------------------------------------ | | Asset not found | Invalid `asset_id` | Check asset exists and is `ready` | | Store not found | Invalid `knowledge_store_id` | Check the knowledge store identifier | ## Responses | Error | Cause | Fix | | ------------------------ | ---------------------------- | --------------------------------------- | | Knowledge store required | Missing `knowledge_store_id` | Add `knowledge_store_id` to the request | | Invalid session | Bad `session_id` | Start a new session (omit `session_id`) | | Input required | Missing `input` array | Add at least one message | # Polling and async status The platform processes content asynchronously. Any time you create an asset or add videos and images to a knowledge store, processing happens in the background. Poll the resource until it reaches the `ready` status before proceeding. ## Polling helper **`Python`** ```python Python maxlines=22 import time def wait_for_ready(fetch, interval=5, timeout=600): """Poll a resource until it is ready or failed. `fetch` returns the current resource, for example: lambda: client.assets.retrieve(asset_id=asset_id) """ elapsed = 0 while elapsed < timeout: resource = fetch() if resource.status == "ready": return resource elif resource.status == "failed": raise Exception(f"Resource failed: {resource.id}") print(f" Status: {resource.status} ({elapsed}s elapsed)") time.sleep(interval) elapsed += interval raise TimeoutError(f"Not ready after {timeout}s") ``` **`Node.js`** ```javascript Node.js maxlines=22 async function waitForReady(fetch, intervalMs = 5000, timeoutMs = 600000) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const resource = await fetch(); if (resource.status === "ready") return resource; if (resource.status === "failed") throw new Error(`Resource failed: ${resource.id}`); console.log(` Status: ${resource.status}`); await new Promise((r) => setTimeout(r, intervalMs)); } throw new Error(`Not ready after ${timeoutMs}ms`); } ``` ## Usage **`Python`** ```python Python maxlines=15 # Wait for an asset asset = wait_for_ready( lambda: client.assets.retrieve(asset_id=""), interval=5 ) # Wait for a knowledge store item (longer interval - indexing takes more time) item = wait_for_ready( lambda: client.knowledge_store_items.retrieve( knowledge_store_id="", item_id="" ), interval=10, timeout=600, ) ``` **`Node.js`** ```javascript Node.js maxlines=15 // Wait for an asset const asset = await waitForReady(() => client.assets.retrieve(""), 5000); // Wait for a knowledge store item (longer interval - indexing takes more time) const item = await waitForReady( () => client.knowledgeStoreItems.retrieve("", ""), 10000, 600000 ); ``` ## Recommended intervals | Resource | Poll Interval | Typical Wait | Timeout | | -------------------- | ------------- | ------------ | ------- | | Asset (direct) | 5s | 10-60s | 120s | | Asset (URL) | 5s | 10-120s | 300s | | Knowledge store item | 10s | 1-10 min | 600s | ## Batch polling Wait for multiple resources in parallel: **`Python`** ```python Python maxlines=22 import concurrent.futures def wait_for_items(client, store_id, item_ids): """Wait for multiple knowledge store items in parallel.""" def wait_one(item_id): return wait_for_ready( lambda: client.knowledge_store_items.retrieve( knowledge_store_id=store_id, item_id=item_id ), interval=10, ) with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: futures = {executor.submit(wait_one, iid): iid for iid in item_ids} return {futures[f]: f.result() for f in concurrent.futures.as_completed(futures)} ``` **`Node.js`** ```javascript Node.js maxlines=22 async function waitForItems(client, storeId, itemIds) { // Wait for multiple knowledge store items in parallel. const results = await Promise.all( itemIds.map((itemId) => waitForReady(() => client.knowledgeStoreItems.retrieve(storeId, itemId), 10000) ) ); return Object.fromEntries(itemIds.map((id, i) => [id, results[i]])); } ``` ## Polling guidance * **Don't poll too aggressively**: 1-second intervals waste rate limit budget * **Always handle `failed`**: failed resources don't recover * **Set a timeout**: don't poll forever if something goes wrong * **Rely on polling**: webhooks are not available during the research preview # Jupyter notebook [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/twelvelabs-io/twelvelabs-developer-experience/blob/main/quickstarts/jockey/guides/error_handling.ipynb) > Handle errors across the Jockey API with typed exceptions, retries, and status polling.