Errors & Pagination
Error taxonomy, retry behavior, idempotency, and list vs listPage pagination in @trickest/sdk.
On this page7
Error classes
Every failed HTTP call throws a subclass of TrickestError. Branch with
instanceof — do not parse status codes from message strings.
| Class | HTTP | When |
|---|---|---|
ValidationError | 400 | Bad request body or SDK-side guard (e.g. TQL limit) |
AuthError | 401 | Missing, invalid, or expired token |
ForbiddenError | 403 | Authenticated but not allowed |
NotFoundError | 404 | Resource does not exist |
ConflictError | 409 | State conflict (duplicate, lock, …) |
RateLimitError | 429 | Rate limited — see retryAfter |
ServerError | 5xx | Backend failure — safe to retry idempotent ops |
NetworkError | — | DNS, connection refused, TLS |
TimeoutError | — | Exceeded timeout or AbortSignal |
Each error exposes:
status— HTTP status (0 for network/timeout)code— machine-readable code stringrequestId/traceId— use in support and log correlation
import {
TrickestError,
AuthError,
NotFoundError,
RateLimitError,
ValidationError,
} from '@trickest/sdk'
try {
await client.workflows.get('wf_missing')
} catch (err) {
if (err instanceof NotFoundError) {
return null
}
if (err instanceof RateLimitError) {
await sleep(err.retryAfter ?? 1000)
return retry()
}
if (err instanceof ValidationError) {
console.error(err.message, err.details)
}
if (err instanceof TrickestError) {
console.error(err.status, err.code, err.traceId)
}
throw err
}Retry model
Retry is on by default: 3 attempts, exponential backoff (1s → 2s → 4s).
| Retried | Not retried |
|---|---|
429 (honors Retry-After, capped at 60s) | 401 |
5xx, network, timeout on idempotent methods (GET, PUT, DELETE) | Non-idempotent POST without idempotencyKey |
Any method when idempotencyKey is set | — |
const client = new TrickestClient({
token: '…',
retry: { maxRetries: 5, baseDelay: 500 },
})
const noRetry = new TrickestClient({ token: '…', retry: false })
await client.runs.execute(workflowId, opts, { retry: false })Pagination
Most list operations expose two shapes:
list() — AsyncGenerator
Auto-fetches pages until exhausted. Default page size is often 500.
for await (const wf of client.workflows.list(spaceId)) {
console.log(wf.name)
}listPage() — single page
Returns PaginatedResponse<T>:
interface PaginatedResponse<T> {
results: T[]
next: string | null
count: number
}
const page = await client.workflows.listPage(spaceId, {
pageSize: 20,
page: 1,
})
console.log(page.results.length, 'of', page.count)Services with both patterns
| Namespace | list | listPage |
|---|---|---|
spaces | yes | yes |
workflows | yes (needs spaceId) | yes |
runs | yes | yes |
storage | yes | yes |
variables | yes | yes |
fleet | yes | — |
library | search() generator | — |
memory | yes | — |
audit | yes (needs vaultId) | — |
Some services only expose one shape — check the method index.