Sume SDK errors by status: which class for 401, 402, 403, 404, 409
The @sume-com/sdk error classes by HTTP status, which fields they carry, and how run helpers differ from generated operations that resolve instead of throwing.

In @sume-com/sdk, each non-2xx status maps to a SumeApiError subclass: 401 SumeAuthenticationError, 402 SumeInsufficientCreditsError, 403 SumePermissionError, 404 SumeNotFoundError, 409 SumeConflictError, 429 SumeRateLimitError, and 5xx SumeServerError. The run helpers are the catch: they always throw SumeRunRequestError, so you branch on its status or code.
The mapping
The Waiting for runs and jobs page lists the subclasses. Every one carries code, requestId, retryable, retryAfterSeconds, nextAction, details, and the raw body.
| HTTP status | Class | Typical next step |
|---|---|---|
| 401 | SumeAuthenticationError | Check the key and send one credential header |
| 402 | SumeInsufficientCreditsError | Add funds or lower the cost |
| 403 | SumePermissionError | Check the scope; scopes are fixed at key creation |
| 404 | SumeNotFoundError | Wrong workspace or another member's job |
| 409 | SumeConflictError | Read the code: job_not_completed, idempotency_conflict, and others |
| 429 | SumeRateLimitError | Wait for retryAfterSeconds |
| 5xx | SumeServerError | Retry with the same Idempotency-Key |
Which calls throw
Generated operations do not throw on an API error. They resolve with { data, error, response }. subscribeFormatRun and waitForRun do throw, because a poll loop has nowhere to put a non-result. waitForJob throws SumeJobTimeoutError or SumeJobRequestError, both carrying jobId.
A failed run is not an exception. The helpers resolve for any terminal status, so read status and error from the receipt.
A catch block that uses the fields
Log the requestId, which is safe to share with Sume support, and never log keys or signed URLs. The sample follows the SDK docs: it checks the status first, then reads retryAfterSeconds or retryable.
import { createSumeClient, subscribeFormatRun, SumeRunRequestError } from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });
try {
const run = await subscribeFormatRun({
client,
path: { handle: "acme", slug: "product-promo" },
idempotencyKey: "order-8823-promo-v1",
body: { input: { product_url: "https://shop.example.com/p/8823" } },
});
console.log(run.status);
} catch (error) {
if (error instanceof SumeRunRequestError) {
if (error.status === 402) console.error("add funds", error.requestId);
else if (error.status === 429) console.error("retry in", error.retryAfterSeconds);
else console.error(error.code, error.retryable, error.requestId);
} else {
throw error;
}
}The client retries for you, within limits
createSumeClient retries 408, 429, 5xx, and transport failures, two times by default, with exponential backoff and jitter, and it honors retry-after. It retries a POST only when the request carries an Idempotency-Key. Without a key, a replay starts and bills a second run, so subscribeFormatRun generates one for you unless you pass your own.
Handling each class
Retry only what is worth retrying. The SDK already retries 408, 429 and 5xx twice with backoff, and retries POST only when an Idempotency-Key is present, so adding your own loop on top multiplies attempts. Do not retry 401, 403 or 404; fix the key, the scope or the id.
A 402 means the wallet cannot cover the reserve. Show the user a top-up path rather than retrying. A 409 means the request conflicts with the current state, such as a cancel after generation started.
Sources
Related posts
More in Developers
- Sume TTS language field: set ja or ko, or the voice reads in English
Omit language on Sume TTS and the provider defaults to English; Sume infers ko or ja only from a Hangul- or kana-only script. Set it on non-English scripts.
- A known-answer test for the Sume webhook signature: one body, one hex
A fixed secret, timestamp, and body give a fixed sume-v1 value. Use it to unit-test your verifier: valid, stale, empty secret, and rotation cases in Python.
- Webhook and poll race on a Sume video: one SQLite insert decides
Keep polling as a backup to the job webhook. Dedupe on job_id with INSERT OR IGNORE so only one path downloads. 10 deliveries 30 s apart cover 270 s.
- Sume webhook delivery statuses: pending to exhausted, and your move
A Sume job webhook has six delivery states, from pending to exhausted. What each means for your receiver, where to read it, and when to redeliver or poll.
Written by Sume