Inngest NonRetriableError and RetryAfterError for Sume errors

Map Sume HTTP errors to Inngest: NonRetriableError for 400, 401, 402 and 404, and RetryAfterError with the retry-after header on a 429.

4 min readSume
All posts

Throw NonRetriableError for Sume errors that cannot succeed on a retry, and RetryAfterError with the delay from the retry-after header when Sume returns a 429. Anything else can use Inngest's default retries.

Inngest facts are from its error-handling and errors reference docs and the v1.45.0 release; Sume facts from Errors and credits and Authentication, read 2026-10-01.

What do the two classes do?

Per the docs, NonRetriableError stops retries, and RetryAfterError controls retry timing. Its second argument accepts milliseconds as a number, a string such as "30m", or a Date. Inngest v1.45.0, released September 17, also made API v2 return 400 for unsupported query parameters or bodies; that is separate from your function errors.

Which Sume status maps to which class?

Sume's error envelope has error.code, message, request_id and details. A 429 names error.details.scope as read or write and sends retry-after.

Sume error to Inngest class, using the Sume docs read 2026-10-01
Sume status and codeInngest throw
400 invalid_requestNonRetriableError
401 unauthorizedNonRetriableError
402 insufficient_creditsNonRetriableError
404 not_foundNonRetriableError
409 idempotency_conflict on submitNonRetriableError
429 rate_limitedRetryAfterError using retry-after
429 queue_full, 503 provider_capacity_exceededDefault retry or a longer RetryAfterError

How do you write the mapping?

One helper takes the SDK's response and error and throws the right class. The fallback of 30 seconds for a missing header is my choice, not a Sume value.

import { NonRetriableError, RetryAfterError } from "inngest";

export function raiseForSume(response: Response, error: unknown): void {
  const status = response.status;
  if (status === 429) {
    const seconds = Number(response.headers.get("retry-after"));
    const ms = Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 30000;
    throw new RetryAfterError("sume rate limited", ms);
  }
  if ([400, 401, 402, 404, 409].includes(status)) {
    throw new NonRetriableError("sume " + status + ": " + JSON.stringify(error));
  }
  if (status >= 400) throw new Error("sume " + status);
}

What about Formats and job errors with retryable?

Format and job errors also carry retryable, retry_after_seconds and next_action. Prefer those fields when present, and fall back to the status mapping above. See the error fields post.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume