Errors
Every SDK method returns `Result<T, CimplifyError>`. Methods do not throw; instead you check `.ok` and branch on `error.code`. Wrap nothing in `try/catch`.
Result type
type Result<T, E = CimplifyError> =
| { ok: true; value: T }
| { ok: false; error: E }CimplifyError
import { CimplifyError } from '@cimplify/sdk'
class CimplifyError {
code: string // 'VALIDATION_ERROR', 'NOT_FOUND', 'RATE_LIMITED', ...
message: string // human-readable
retryable?: boolean // safe to retry?
context?: Record<string, unknown> // structured details (field name, validation rule, ...)
}Read errors
const r = await client.catalogue.getProducts()
if (!r.ok) {
console.error(r.error.code) // machine-readable
console.error(r.error.message) // human-readable
console.error(r.error.retryable) // boolean
console.error(r.error.context) // optional structured detail
return
}
console.log(r.value.items)Branch on code
const r = await client.cart.addItem({ item_id: 'prod_xxx', quantity: 1 })
if (!r.ok) {
switch (r.error.code) {
case 'OUT_OF_STOCK':
showToast('This item is out of stock')
break
case 'ITEM_UNAVAILABLE':
showToast('No longer available')
break
case 'VALIDATION_ERROR':
showToast(r.error.message)
break
case 'RATE_LIMITED':
showToast('Slow down; try again in a moment')
break
default:
showToast(r.error.message)
}
}Retry helper
Only retry when the server has marked the error retryable. Use exponential backoff and a small attempt cap.
import type { Result } from '@cimplify/sdk'
async function withRetry<T>(
fn: () => Promise<Result<T>>,
maxAttempts = 3,
): Promise<Result<T>> {
let last!: Result<T>
for (let i = 0; i < maxAttempts; i++) {
last = await fn()
if (last.ok) return last
if (!last.error.retryable) return last
await new Promise((resolve) => setTimeout(resolve, 250 * 2 ** i))
}
return last
}
const r = await withRetry(() => client.catalogue.getProducts({ limit: 24 }))Common error codes
| Code | Retryable | Meaning |
|---|---|---|
VALIDATION_ERROR | No | Bad input; fix the request |
NOT_FOUND | No | Resource missing or filtered out |
UNAUTHORIZED | No | Sign in / refresh the session |
FORBIDDEN | No | Authenticated but not allowed |
RATE_LIMITED | Yes | Back off and retry |
NETWORK_ERROR | Yes | Transport failed before response |
TIMEOUT | Yes | Server did not respond in time |
SERVER_ERROR | Yes | Generic 5xx |
Checkout-specific codes
| Code | Recoverable | Meaning |
|---|---|---|
INVALID_CART | No | Cart is empty or no longer valid |
ALREADY_PROCESSING | No | A checkout for this cart is already in flight |
PAYMENT_FAILED | Yes | Provider declined; let the user retry |
FX_QUOTE_FAILED | Yes | Could not lock an FX quote for the pay currency |
AUTH_INCOMPLETE | Yes | Customer must finish OTP / PIN authorization |
AUTH_LOST | Yes | Session expired mid-checkout |
CHECKOUT_FAILED | Sometimes | Generic terminal failure; read message + retryable |
Schema-shape errors (testing only)
Inside the testing harness, assertX helpers throw SchemaViolationError with structured issues[]. This is the only place the SDK throws.
import { assertCart, SchemaViolationError } from '@cimplify/sdk/testing'
try {
assertCart(response)
} catch (e) {
if (e instanceof SchemaViolationError) {
console.error(e.toJSON()) // RFC 7807 + structured issues[]
}
}Related
-
Result<T, E> Why methods never throw, how to narrow
-
Idempotency Safe retries for write methods
-
Checkout Where most error codes show up
-
Auth UNAUTHORIZED + AUTH_LOST recovery
Link
client.link covers saved addresses, saved mobile money, link preferences, and sessions. Customer-scoped surface routed through the SDK's separate linkApiUrl transport, not the per-business storefront API.
Component catalog
90+ React components ship in `@cimplify/sdk/react`. This page covers the page components you mount on routes, the cart drawer, and the most-used primitives. Every component is also [ejectable](/docs/cli/components) via ` cimplify add <name>` when you need full control.