Skip to the documentation
TypeScript

Errors

Every failure throws. Two classes, and a request id on every API error.

Catching one

catch-errors.ts
import { NextmailApiError, NextmailNetworkError, nextmail } from '@nextmail/sdk' try {  await nextmail.emails.send(message)} catch (error) {  if (error instanceof NextmailApiError) {    if (error.isValidation) console.error(error.code, error.param, error.message)    if (error.isPermission) console.error(await nextmail.addresses.list())    if (error.isRateLimited) console.error('try again in', error.retryAfterSeconds, 'seconds')     console.error(error.status, error.requestId)  }   if (error instanceof NextmailNetworkError && error.isTimeout) console.error('no answer in time')   throw error}

A permission_error on a send is usually the KEY’s send scope, a domain or an address it was not given, rather than the workspace, which is why the sample prints what addresses.list() says this key may send as.

The classes

ClassWhen
NextmailApiErrorThe API answered, and not with a success. Carries status, type, code, param, docUrl, requestId and retryAfterSeconds.
NextmailNetworkErrorNo response arrived: DNS, TLS, a dropped connection, the timeout, or your own AbortSignal. Carries cause, and isTimeout is true when the timeout was the reason.
ErrorThrown before anything is sent: a missing or malformed key, an unusable baseUrl, a browser, an empty id.
GetterTrue when
isAuthtype is authentication_error, a 401: no key, the wrong kind of credential, or a key we did not issue.
isPermissionpermission_error, a 403: a real key without the scope or the From address it needs.
isScopeMissingcode is insufficient_scope, the 403 that names a missing scope.
isInvalidRequestinvalid_request_error, a 400: a request that could not be understood. A message over the size ceiling comes back as a 422 message_too_large, so isValidation is the getter that catches it.
isValidationvalidation_error, a 422: the schema refused it, and param names the field.
isNotFoundnot_found_error, a 404: no such resource.
isConflictconflict_error, a 409: the resource is past the point where this could be done to it.
isRateLimitedrate_limit_error, a 429. retryAfterSeconds holds the wait when the server named one.
isServerErrorstatus is 500 or above. Quote requestId if you contact support.
isRetryablestatus is 408, 429, 500, 502, 503 or 504.
isStepUpRequiredcode is step_up_required, the 403 an OAuth access token gets before a sensitive change until the person has verified a code. Added in 0.0.6.

The getters read type, the frozen half of the envelope. code stays a string, because the API guarantees it is open and additive, so treat one you do not recognise as its type. A closed union would make an SDK upgrade the price of reading a new failure mode.

A body that is not the API’s error envelope still becomes an NextmailApiError, with type inferred from the status and code set to unrecognised_response. A success whose body is not JSON throws one too.

An abort is an NextmailNetworkError as well, whether it lands during the request or during the wait before a retry, and the abort is kept on cause. Check signal.aborted when you need to tell your own cancellation from a network failure.

requestId

Every NextmailApiError carries the request id the server sent, from the error body or the x-request-id header, and it is the only thing tying your failure to a line in the server's log. A success resolves to the parsed body alone, so there is no request id to read on one.