axios rejects Sume 409 and 429: read response.data.error.code

axios rejects any status outside 200-299 by default, so a Sume 409 or 429 throws. Read error.response.data.error.code, and use AbortSignal for timeouts.

5 min readSume
All posts

Short answer

By default axios rejects the promise when the status code is outside 200 to 299, per its request config docs. A Sume 409, 429 or 402 therefore throws, and the useful part is in error.response.data.error.code. Pass an AbortSignal for cancellation, since the docs say the signal option lets you cancel with the AbortController API.

What the error object holds

Sume error bodies have one shape: an error object with code, message, request_id and details. In axios that body is on error.response.data, and the status is on error.response.status. Branch on the code, not the message text, because the code is the stable part.

Match the code to a decision. The common ones are listed on the Sume errors page, and a handful drive almost every retry choice.

Sume error codes and the usual client decision (read 2026-10-03)
StatusCodeDecision
400invalid_requestFix the request; do not retry
401unauthorizedFix the credential; send only one of Authorization or x-api-key
402insufficient_creditsAdd balance or wait; do not retry
404not_foundCheck the id and the key's member
409job_not_completed and relatedKeep polling or read the job record
429rate_limited or queue_fullWait for retry-after, then retry with the same key
503provider_capacity_exceededRetry later with backoff

Handling it

Catch the error, check axios.isAxiosError, and pull the code out of the response. The sample submits an image job with an Idempotency-Key and prints the code on failure. Note that timeout is documented in milliseconds and aborts the request when exceeded; the docs do not state a default, so set one explicitly rather than assuming.

A timeout or abort stops your wait, not the job. The submit may already have been accepted, so retry only with the same Idempotency-Key.

import axios from "axios";

const key = crypto.randomUUID();
try {
  const { data } = await axios.post(
    "https://api.sume.com/v1/image-1.0/generate",
    { prompt: "A matte black bottle on marble", mode: "async" },
    {
      headers: { "x-api-key": process.env.SUME_API_KEY, "Idempotency-Key": key },
      timeout: 30000,
      signal: AbortSignal.timeout(35000),
    },
  );
  console.log(data);
} catch (err) {
  if (axios.isAxiosError(err) && err.response) {
    console.error(err.response.status, err.response.data?.error?.code);
  } else {
    throw err;
  }
}

Using validateStatus

The same docs describe validateStatus. If you set it to accept 409, axios resolves instead of throwing and you inspect the status yourself. That can be convenient for a poller that treats job_not_completed as a normal answer, but it moves the burden onto you: every call site must check the status before reading data. A narrow validateStatus, scoped to the one request that expects a 409, is safer than a global change.

Retries are a separate layer

axios itself does not decide whether to retry. If you add a retry plugin, apply the same rule as the Sume SDK: replay GET and HEAD freely, and replay POST only when it carries an Idempotency-Key. Do not retry 400, 401, 402 or 404, and honor retry-after on a 429. Then poll the status route until terminal is true rather than waiting inside one request.

Logging without leaking

When you log a failure, include error.request_id and the status, and leave out the headers. The request headers hold your API key, and axios error objects can carry the request config. Log a small object you build yourself rather than the raw error, so the key never lands in a log aggregator. The request_id is the piece to quote when you ask for help diagnosing a failure.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume