Errors and retries
Which errors to retry, how long to wait, and how to make a retry safe with an idempotency key.
Most errors mean the request itself must change, and sending it again gives the same answer. A few mean "not now": those are worth retrying, after the wait the API tells you.
You need: an API key in TYPESAFE_API_KEY. Set TYPESAFE_BASE_URL only if you call a gateway other than https://api.sieve.one.
Retry or fix
| Status | Retry? | What to do |
|---|---|---|
| 400, 404, 405, 413, 415, 422 | No | Fix the request: model name, body shape, size or encoding. The body says what is wrong. |
| 401 | No | Check the key. A missing, mistyped, expired or revoked key is always 401. |
| 403 | No | The key is valid but not allowed: IP allowlist, access level, model list, or a suspended organisation. |
429 with retry-after | Yes | A rate limit. Wait retry-after seconds, then retry. |
429 without retry-after | Not soon | No credit left or a daily limit reached. Top up or wait for the daily reset. |
| 500, 504 | Yes | Retry with exponential backoff. |
| 503, 529 | Yes, unless x-should-retry: false | Model briefly unavailable or overloaded. Wait retry-after if present. x-should-retry: false means it was switched off on purpose. |
Every response carries a request ID: x-typesafe-request-id on System One and /v1/models, x-request-id on /v1/decisions. Log it, and quote it when you write to support@sthanika.ai. The full list of bodies is on Errors.
Match on the status code and error type
error_type (System One) and error.type / error.code (/v1/decisions) are stable. The message is for people and may be reworded.
Make retries safe
Send an Idempotency-Key header with a value you generate once per logical request and reuse on every retry of it. A second request with the same key from the same API key within 60 seconds of a success gets the first response back (with Sieve-Idempotent-Replayed: true) instead of being answered and billed again. Without the header, identical bodies from the same key within 60 seconds are also de-duplicated.
A retry loop
The examples use only the standard library of each language. They retry 429 (only with retry-after), 500, 503, 504, 529 and network errors, at most four attempts, and stop at once on everything else.
import json
import os
import random
import time
import urllib.error
import urllib.request
import uuid
BASE_URL = os.environ.get("TYPESAFE_BASE_URL", "https://api.sieve.one")
API_KEY = os.environ["TYPESAFE_API_KEY"]
RETRYABLE = {429, 500, 503, 504, 529}
def system_one(body, max_attempts=4):
data = json.dumps(body).encode()
idempotency_key = str(uuid.uuid4()) # the same key on every attempt
for attempt in range(1, max_attempts + 1):
request = urllib.request.Request(
f"{BASE_URL}/v1/systemone",
data=data,
method="POST",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"Idempotency-Key": idempotency_key,
},
)
try:
with urllib.request.urlopen(request, timeout=60) as response:
return json.load(response)
except urllib.error.HTTPError as error:
detail = error.read().decode()
retry_after = error.headers.get("retry-after")
request_id = error.headers.get("x-typesafe-request-id")
give_up = (
error.code not in RETRYABLE
or attempt == max_attempts
or error.headers.get("x-should-retry") == "false"
or (error.code == 429 and retry_after is None) # no credit or a daily limit
)
if give_up:
raise RuntimeError(f"HTTP {error.code} (request {request_id}): {detail}") from None
delay = float(retry_after) if retry_after else min(2 ** attempt, 20) * random.uniform(0.5, 1.0)
except (urllib.error.URLError, TimeoutError):
if attempt == max_attempts:
raise
delay = min(2 ** attempt, 20) * random.uniform(0.5, 1.0)
time.sleep(delay)
answer = system_one({
"model": "sieve-latest",
"state": "I was charged twice. Please fix this ASAP.",
"questions": {"billing": {"type": "noul", "instructions": "Is this message about billing?"}},
})
print(answer["answers"]["billing"]["noul"])The TypeSafe SDKs already retry for you (2 retries by default, on 408, 429 and 5xx including 529, honouring retry-after). They also retry a 429 for an empty balance, which keeps failing until credit is added, so handle that case in your code. See SDKs. Use the loop above when you call the HTTP API directly.
See what an error looks like
Send an unknown model to see a 400 without spending credit:
curl -i https://api.sieve.one/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "nope", "state": "x", "questions": {"q": {"type": "noul", "instructions": "y"}}}'{ "detail": { "error_type": "api_usage_error", "message": "Unknown model: nope" } }On /v1/decisions the same mistake uses the OpenAI shape:
{ "error": { "message": "Unknown model: nope", "type": "invalid_request_error", "param": "model", "code": "model_not_found" } }Errors in the console
The console and its API answer every error with one shape:
{ "error": { "code": "TOPUP_BELOW_MINIMUM", "message": "That amount is below the minimum top-up. Enter a larger amount.", "fields": { "amount": "The minimum top-up is 500 INR, before GST." } } }codeis stable: one meaning per code. Match on it, not onmessage.messageis a short sentence that says what to do. It may be reworded.fieldsnames the form fields to fix.retryAfter(also theRetry-Afterheader) says how many seconds to wait.- Error pages show a request ID. Quote it when you contact support.
| Code | HTTP | Message | What to do |
|---|---|---|---|
INVALID_CREDENTIALS | 401 | Email or password is incorrect. Try again, or reset your password. | Retry, or reset the password. |
WRONG_PASSWORD | 400 | That password is incorrect. Try again. | Re-enter the current password. |
INVALID_CODE | 400 | That code didn't work. Check it and try again. | Enter a fresh code. |
EMAIL_NOT_VERIFIED | 403 | Verify your email first. Check your inbox for the link. | Open the verification link. |
PASSWORD_TOO_WEAK | 400 | Choose a stronger password, like a phrase of four or more words. | Choose a longer phrase. |
PASSWORD_TOO_SHORT | 400 | Use at least 8 characters (12+ recommended). | Use 8+ (12+ recommended). |
PASSWORD_TOO_LONG | 400 | Use at most 128 characters. | Shorten it. |
INVALID_EMAIL | 400 | Enter a valid email address, like name@company.com. | Fix the address. |
PASSWORD_CONTAINS_EMAIL | 400 | Don't use your email address in your password. | Choose another password. |
PASSWORD_TOO_COMMON | 400 | That password is too common. Try a longer phrase. | Choose another password. |
PASSWORD_BREACHED | 400 | That password has appeared in a data breach. Choose another. | Choose another password. |
TOKEN_EXPIRED | 410 | This link has expired. Ask for a new one below. | Request a new link. |
TOKEN_ALREADY_USED | 410 | This link has already been used. Ask for a new one if needed. | Request a new link if needed. |
TOKEN_INVALID | 400 | This link isn't valid. Ask for a new one below. | Request a new link. |
RATE_LIMITED | 429 | Too many requests. Wait a moment, then try again. | Wait retryAfter seconds. |
GATEWAY_UNAVAILABLE | 503 | The Sieve API isn't responding. Try again in a moment. | Retry shortly. |
OAUTH_STATE_MISMATCH | 400 | Sign-in timed out or was opened in another tab. Try again. | Start sign-in again in one tab. |
OAUTH_EMAIL_UNVERIFIED | 400 | Your provider didn't confirm your email. Try another way to sign in. | Use another sign-in method. |
OAUTH_FAILED | 400 | We couldn't sign you in with that provider. Try again. | Retry or use email and password. |
SESSION_EXPIRED | 401 | Your session has expired. Please sign in again. | Sign in again. |
REAUTH_REQUIRED | 403 | For your security, sign in again to do this. | Sign in again, then retry. |
FORBIDDEN | 403 | You don't have permission to do that. | Follow redirect, or reload the page. |
OWNER_ONLY | 403 | Only admins can do this. Ask an admin of your organisation. | Ask an organisation admin. |
LAST_OWNER | 409 | An organisation needs at least one admin. Make someone else an admin first. | Make someone else an admin first. |
NOT_FOUND | 404 | That item no longer exists. Reload the page. | Reload the page. |
VALIDATION | 400 | Some fields need fixing. Check the highlighted fields. | Fix the named fields. |
INVITE_ONLY | 403 | Sign-up is invite-only for now. Ask an admin of your organisation to invite you. | Ask an admin for an invite. |
DISPOSABLE_EMAIL | 400 | Disposable emails aren't allowed. Use a work or personal email. | Use a work or personal email. |
INVITE_INVALID | 400 | This invite has expired or been cancelled. Ask for a new one. | Ask for a new invite. |
INVITE_WRONG_ACCOUNT | 403 | This invite is for a different email. Sign in with that address. | Sign in as the invited email. |
ALREADY_MEMBER | 409 | You're already a member of this organisation. | Nothing; open the org. |
ORG_LIMIT | 409 | You can belong to at most 10 organisations. Leave one first. | Leave an organisation first. |
PASSKEY_CANCELLED | 400 | Passkey request cancelled. Try again when you're ready. | Retry when ready. |
PASSKEY_FAILED | 400 | That passkey didn't work. Try again, or use your password. | Retry, or use the password. |
PASSKEY_EXISTS | 409 | This passkey is already registered on your account. | Nothing to do. |
INTERNAL | 500 | Sieve hit an unexpected error. Try again, or contact support. | Retry; contact support with the request ID. |
ACCOUNT_SUSPENDED | 403 | This account is suspended. Contact Sieve support to restore access. | Contact support. |
PASSWORD_CHECK_UNAVAILABLE | 503 | We couldn't check that password right now. Try again in a minute. | Retry in a minute. |
BODY_NOT_JSON | 415 | Send the request body as JSON. | Send JSON. |
BODY_TOO_LARGE | 413 | The request is too large. Send less data and try again. | Send less data. |
BODY_MALFORMED | 400 | The request body isn't valid JSON. Fix it and try again. | Fix the JSON. |
NOT_ORG_MEMBER | 403 | You're not a member of this organisation. Switch organisation. | Switch organisation or reload. |
MEMBER_NOT_FOUND | 404 | That person is no longer a member. Reload the page. | Reload the members page. |
CANNOT_REMOVE_SELF | 409 | You can't remove yourself. Use Leave organisation instead. | Use Leave organisation. |
MEMBER_LIMIT_REACHED | 409 | This organisation is full (200 members). Ask an admin to make room. | Ask an admin to remove someone. |
INVITE_NOT_FOUND | 404 | That invite no longer exists. Reload the page. | Reload the page. |
INVITE_PENDING_LIMIT | 409 | 100 invites are pending. Cancel some before inviting more. | Cancel old invites. |
KEY_NOT_FOUND | 404 | That key no longer exists. Reload the page. | Reload the keys page. |
KEY_NOT_ACTIVE | 409 | This key is already revoked or expired. Reload the page. | Reload; create a new key. |
KEY_CREATOR_LEFT | 409 | This key's creator left the organisation. Revoke it instead. | Revoke it; create a service key. |
TOPUPS_DISABLED | 403 | Self-serve top-ups aren't available yet. Contact us to add credits. | Contact support to add credits. |
TOPUP_BELOW_MINIMUM | 400 | That amount is below the minimum top-up. Enter a larger amount. | Enter a larger amount. |
TOPUP_REJECTED | 400 | We couldn't start this top-up. Check the amount and try again. | Fix the amount or contact support. |
TOPUP_CHANGED | 409 | This top-up changed while it was being sent. Reload and try again. | Reload and retry. |
TAX_PROFILE_REQUIRED | 409 | Add your legal name, address and state in Billing → Tax, then try again. | Complete Billing → Tax. |
TAX_PROFILE_UNAVAILABLE | 503 | Tax profiles are unavailable. Contact support before adding credits. | Contact support. |
ORG_INACTIVE | 403 | This organisation isn't active. Contact support to restore it. | Contact support. |
PAYMENT_LIMIT_REACHED | 429 | Too many unfinished payments. Finish one, or try again in an hour. | Finish one payment or wait. |
PAYMENTS_UNAVAILABLE | 503 | Payments are unavailable right now. Try again in a minute. | Retry in a minute. |
AUTO_RECHARGE_UNAVAILABLE | 403 | Auto-recharge isn't available yet. Add credits by hand for now. | Top up by hand. |
EXPORT_COOLDOWN | 429 | An export was already requested. Try again after 24 hours. | Wait for the cooldown. |
EXPORT_NOT_FOUND | 404 | This export expired or was removed. Request a new one. | Request a new export. |
DELETION_BLOCKED | 409 | Deletion is blocked. Resolve the listed items, then try again. | Resolve the listed items. |
DELETION_NOT_PENDING | 409 | No pending deletion request was found. | Nothing to do. |
DELETION_ALREADY_SCHEDULED | 409 | Deletion is already scheduled. | Nothing to do; cancel if needed. |
SETTLEMENT_IN_PROGRESS | 409 | Payments are still settling. Wait for them to finish, then retry. | Wait a few minutes, retry. |
WALLET_FROZEN | 409 | The wallet is frozen. Contact support before closing this organisation. | Contact support. |
FINANCIAL_RECORDS_UNAVAILABLE | 503 | Financial records are unavailable. Try again later or contact support. | Retry later or contact support. |
SUPPORT_REQUEST_LIMIT | 429 | Too many support requests this hour. Reply to our email to add details. | Reply to the support email instead. |
Next steps
Last updated