Skip to content

Error Handling

Copy page

Every SDK rejects with typed errors so you can branch on failure mode instead of parsing strings. The client SDK has the richest hierarchy; the server and MCP packages each have a focused error type.

All client errors extend DivinciError, which carries the machine-readable code, the HTTP status, an optional details object, and the requestId for support.

| Error | Status | Thrown when | |-------|--------|-------------| | AuthenticationError | 401 | Invalid, missing, or expired credentials | | ValidationError | 400 | Invalid request parameters (see fieldErrors) | | RateLimitError | 429 | Too many requests (see retryAfter, limit, remaining) | | QuotaExceededError | 429 | Usage quota for the workspace/release is exhausted | | PaymentRequiredError | 402 | An x402-priced resource needs payment | | NotFoundError | 404 | The release, thread, or resource doesn't exist | | NetworkError | — | Connection failed (see retryable) | | TimeoutError | — | The request exceeded timeout | | StreamError | — | A streaming response failed mid-flight |

import {
DivinciClient,
RateLimitError,
QuotaExceededError,
AuthenticationError,
PaymentRequiredError,
} from "@divinci-ai/client";
try {
await client.chat.send(thread.id, "Hello!", { assistantName: "your-assistant-id" });
} catch (error) {
if (error instanceof RateLimitError) {
// Back off using the server-provided hint
await sleep((error.retryAfter ?? 1) * 1000);
return retry();
}
if (error instanceof QuotaExceededError) {
showUpgradePrompt();
return;
}
if (error instanceof AuthenticationError) {
redirectToLogin();
return;
}
if (error instanceof PaymentRequiredError) {
// amount + payment URL are available on the error
openCheckout(error.getPaymentUrl(), error.getAmountUsd());
return;
}
throw error; // unknown — let it bubble
}

Every DivinciError serializes cleanly for logging:

import { DivinciError } from "@divinci-ai/client";
try {
await client.chat.create();
} catch (error) {
if (error instanceof DivinciError) {
console.error(error.toJSON()); // { code, status, details, requestId, message }
}
}

The server SDK throws ServerApiError for any non-2xx API response. It exposes the same fields — code, status, details, requestId, plus the raw body.

import { ServerApiError } from "@divinci-ai/server";
try {
await divinci.workspaces.get("ws_missing");
} catch (error) {
if (error instanceof ServerApiError) {
if (error.status === 404) {
// handle not found
}
console.error(error.code, error.requestId);
}
}

Paid tool calls throw PaymentRequiredError (carrying paymentDetails) when no valid payment accompanies the request. See MCP x402 Payments for the autopay and manual-retry patterns.

  • NetworkError — retry only if error.retryable is true.
  • RateLimitError — honor retryAfter; use exponential backoff if absent.
  • TimeoutError — safe to retry idempotent reads; be careful retrying sends.
  • AuthenticationError / ValidationError — never retry blindly; fix the cause.

The SDKs already retry transient failures internally up to maxRetries (default 3). Tune it per client via the constructor, or pass skipRetry on an individual request through the underlying HTTP client.