Repository navigation
Troubleshooting
These come from the SDK itself (httpStatus is 0), before or instead of a
server response:
| Code | Meaning | Fix |
|---|---|---|
MISSING_API_KEY |
new Client({...}) without an apiKey
|
Pass your 2dai_sk_… key |
NO_FETCH |
No global fetch in this runtime |
Node ≥18, or pass fetch in ClientOptions
|
NETWORK_ERROR |
The request never reached the API (DNS, offline, TLS) | Check connectivity / baseUrl
|
TIMEOUT |
One HTTP call exceeded timeoutMs (default 60 s) |
Raise timeoutMs, check network |
ABORTED |
Your AbortSignal fired mid-request |
Expected — you cancelled |
CLIENT_TIMEOUT |
waitFor/blocking generate hit its wait deadline |
Raise timeoutMs in SubmitOptions/WaitOptions; the job may still finish server-side |
BAD_RESPONSE |
2xx with a non-JSON body (proxy in the way?) | Check baseUrl points at the API, not a web page |
NO_CDN_ID |
cdn.* couldn't extract a CDN id from the argument |
Pass a Creation/result object or the id string |
NO_CREATION_ID |
A creations.* op couldn't resolve a creation id |
Same idea |
UNSUPPORTED |
Node-only option used in a browser (path, savePath) |
Use data/base64 and toBlob/toDataUrl
|
HTTP_<status> |
Server error without a machine code | Inspect err.httpStatus / err.details
|
Server-side codes (INSUFFICIENT_CREDIT, KEY_SPEND_LIMIT_REACHED,
MISSING_SCOPE, NSFW_REJECTED, …) are covered in Errors.
- Never ship a key in public client code — anyone reading your bundle can spend your credit. Browser use is for internal tools/dashboards you control.
- CORS: the API allows browser calls with the
Authorizationheader; if you proxy it, forward that header. -
uploads.image({ path })anddownload(..., { savePath })are Node-only (they throwUNSUPPORTED); use{ data: file }from an<input type="file">andcdn.toBlob()/cdn.toDataUrl()instead. -
cdn.url()loads in an<img>only for public creations — private ones need the key, so go throughtoDataUrl/toBlob.
- Idempotent GETs auto-retry on
429/5xxup tomaxRetries(default 2) with exponential backoff, honouring a serverRetry-Afterheader. Generation submits never auto-retry (they may charge); the server de-duplicates YOUR immediate retry of an identical call by itself (~5 s window). Pass a hexclientToken([0-9a-f]{1,64}) to set the window. - A
RateLimitErrorthat survives the retries means sustained pressure — space out your calls. -
creations.listreturns at most 100 rows per call (limitclamped to 1..100, default 20). Page with the cursor:
let before: string | undefined;
do {
const page = await client.creations.list({ limit: 100, beforeDate: before });
handle(page.creations);
before = page.nextBeforeDate;
} while (before);generate.* waits up to timeoutMs (default 5 min). Heavy qualities can be
slow at peak times, and when all render capacity is busy a job waits in the
queue (pending) until capacity frees up — it ends expired and is refunded if
none does before its queue deadline. If your wait times out, the job usually
still completes:
const state = await client.queue.get(queueId); // 'processing' | 'completed' | …Two kinds of rows can't be re-derived, by design: raw uploads (nothing was
generated, there are no parameters to replay) and wallpaper-resize outputs
(their pricing dimension isn't stored on the creation). Both come back as a
400 ValidationError with REGEN_UNSUPPORTED_TOOL. REGEN_MISSING_SOURCE /
REGEN_MISSING_PROMPT mean the stored row lost what its tool needs — usually
because a reference was permanently deleted. Re-submit through the normal
generate.* method instead.
It shouldn't: settled queue rows are archived (never deleted) once they age
past your newest ~200, and queue.get reads the archive transparently. A
NotFoundError on a stored id means the id is wrong or belongs to another
account — not that it expired.