When something goes wrong.
Errors come back as a JSON envelope with a stable type, a human message, and a request_id you can send us when reporting an issue.
{"type": "error", "error": {
"type": "authentication_error",
"message": "Invalid or revoked API key.",
"request_id": "063f8b83-eee2-4383-a5cf-11e4bcd29d7c"
}}| HTTP | Meaning | What to do |
|---|---|---|
| 400 | Malformed body (missing or wrong-type field), or a store id outside the allowed format on create | The message names the exact field - fix and retry. |
| 401 | Invalid or revoked API key | Check the key; issue a new one in the console. |
| 402 | Out of balance, no card on file, or a tier cap | Top up or add a card in the console. The response carries balance_cents and floor_cents, so you can tell which one stopped you. |
| 403 | The selected model does not support an option you sent (image, max_images, verify, form) | Pick a model that supports it; GET /api/v1/models lists them. The call is refused so you are not charged for something that would not run. |
| 404 | No such memory, store, or image | Check the id. get_image also answers 404 when the memory exists but carries no image. A store id that differs from an existing store only by punctuation also answers 404. |
| 409 | A name clash, or a write already in flight | A store id that differs from an existing store only in ., _ or - cannot be created, so pick another id. Creating the same id again is not a conflict: it answers 200 with "exists". Workspace names are unique within an account. A second write to one store within 100 ms is refused and nothing is stored: the SDKs retry it after the wait the service asks for, and over HTTP you retry it yourself. |
| 413 | Request body over 10MB | Base64 runs about 33% larger than the file it encodes, so resize the image before encoding it. |
| 429 | Rate limited | The SDK already retries these for you, with backoff and jitter, honouring Retry-After. Getting one means the retries ran out - lower your concurrency rather than wrapping a loop of your own around it. |
| 501 | This model's engine does not implement that endpoint | Images and revision history need a newer engine. GET /api/v1/models lists which models serve what. |
| 5xx | Server-side problem | Retry with backoff, but not blindly. The SDKs retry 502, 503, 504 and 408 on calls that only read, such as search, recall and listing stores, on the DELETE calls, and on the memory writes, which carry an idempotency key so a repeat is answered with the first result instead of storing twice. Other writes sent as POST are not retried on these statuses because the server may already have done the work. When you resend a write yourself, give it an idempotency key so a repeat cannot double-write. A 503 with restoring: true means an idle store is being brought back; retry shortly. Include request_id if you contact us. |
Every error is a WosError, and each status also has a class of its own - BadRequestError, AuthenticationError, PaymentRequiredError, PermissionDeniedError, NotFoundError, ConflictError, RateLimitError, GoneError, ServerError, APIConnectionError. Catch the one you mean to handle instead of comparing numbers. In Python and TypeScript an error also carries the service's error type and the rest of its error object as details. A 413 or 422 is a BadRequestError. A retired model answers 410, raised as GoneError (in Rust, is_gone() is true): name a live model instead, from list_models(). Until it retires, a model scheduled to retire names the day in a Sunset header on its answers, and the SDKs warn once with that date.
# SDK error handling (Python) from wontopos import Client, WosError, RateLimitError, PaymentRequiredError, GoneError try: mem.search("...", user_id="alice") except PaymentRequiredError: ... # 402 - top up except RateLimitError: ... # 429 - the SDK already retried; slow down except GoneError: ... # 410 - the model retired; pick one from list_models() except WosError as e: ... # e.status, e.message, e.request_id except (ValueError, TypeError): ... # never left the client
Some mistakes never reach us. A blank store id, a malformed API key or idempotency key, a malformed image argument, and a metadata key that names a store or an idempotency key (userId, store_id, idempotencyKey, in any case or spelling) are refused before the request goes out. Those raise ValueError or TypeError, not WosError, so an except WosError on its own will not catch them. A store id in the wrong format, such as an email address, only logs a warning here; the service refuses it on create with a 400.
Rate limits are per account, shared across all your keys, and scale with your tier - see Usage tiers. Your account's usage is shown in the console.