Sume SDK 402 insufficient credits: it is returned, not thrown
Generated Sume SDK calls resolve with data, error and response. Turn a 402 into SumeInsufficientCreditsError with toSumeApiError and stop retrying.

How do I catch a 402 from the Sume SDK?
You do not catch it, because generated operations such as generateVideoV1 resolve with { data, error, response } and never throw on an HTTP error. Read error, pass its status and body to toSumeApiError, and test the result with instanceof SumeInsufficientCreditsError.
A 402 means the workspace balance cannot fund the estimate that Sume reserves at submit. Nothing ran and nothing was charged. The only fix is to add funds, so a retry loop is wasted effort: the same request returns the same answer until the balance changes.
Which error class means what
toSumeApiError builds a typed error from the envelope every /v1/ failure returns. It keeps code, retryable, retryAfterSeconds and nextAction, so you branch on fields and not on message text.
| Status | SDK class | What to do |
|---|---|---|
| 402 | SumeInsufficientCreditsError | Stop, add funds, then resubmit |
| 429 | SumeRateLimitError | Wait for retryAfterSeconds, same key |
| 409 | SumeConflictError | Do not retry; read the code |
| 5xx | SumeServerError | Retry later with the same key |
Branch on the typed error
This submit helper returns the job id or throws a class you can handle. The check on nextAction is informational: for a 402 the envelope names add_funds.
import {
createSumeClient, generateVideoV1, toSumeApiError,
SumeInsufficientCreditsError, SumeRateLimitError,
} from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY });
export async function submit(orderId, prompt) {
const { data, error, response } = await generateVideoV1({
client,
headers: { "idempotency-key": `order-${orderId}` },
body: { prompt, mode: "async" },
});
if (!error) return data.data.request_id;
const err = toSumeApiError(response?.status, error, "submit failed");
if (err instanceof SumeInsufficientCreditsError) {
console.error("out of credits:", err.nextAction, err.requestId);
} else if (err instanceof SumeRateLimitError) {
console.error("rate limited, wait", err.retryAfterSeconds, "s");
}
throw err;
}Check the balance before a batch
GET /v1/balance returns the balance and GET /v1/usage is the ledger. Sume reserves the provider list price times 1.25 for each job at submit, so a batch of 30 second clips can fail on its fifth submit when the balance only covered four. Read the balance first, compare it with your own estimate, and submit only what the balance funds.
Log requestId from the typed error, so a failed submit can be traced later.
Keep the handler small: one place converts the raw error into a typed error, and the rest of your code branches on the class. That keeps a 402 from being retried by a generic catch-all, and it keeps a 429 from being reported to a customer as a failure when a short wait would have fixed it. A job that never started also has no result to clean up, so on a 402 you only need to tell the person who owns the account that funds are needed.
Sources
Related posts
More in Developers
- Sume SDK idempotencyKey: null sends no key, so POST retries stop
subscribeFormatRun mints a UUID Idempotency-Key by default. Pass null and the create call carries no key, so the SDK will not retry it on a 429 or 5xx.
- Sume SDK maxRetries: 0 when your job queue already retries the call
The SDK retries 408, 429 and 5xx twice by default. Under a queue with five attempts that is up to 15 tries. Set maxRetries to 0 and let one layer retry.
- Does the Sume SDK retry a failed video submit? Only with a key
createSumeClient retries GET and HEAD by default and retries a POST only when an Idempotency-Key header is set. See what is retried, what is not, and why.
- Sume STT mode sync: transcribe a short clip in one request
Send mode sync with wait_timeout_seconds up to 30. A short clip answers 200 with the finished job. A longer one answers 2xx with the queued job to poll.
Written by Sume