Skip to the documentation
Java

Errors and retries

An exception class for every kind of refusal, and retries that cannot send twice.

Handling an error

Errors.java
try {    client.templates().send("order-shipped", Body.of(        "from", "[email protected]",        "to", "[email protected]",        "props", Body.of("orderId", "AC-4192")    ));} catch (ValidationException error) {    System.err.println(error.param() + " " + error.getMessage());} catch (RateLimitException error) {    System.err.println("Wait " + error.retryAfterSeconds() + " seconds");} catch (ApiException error) {    System.err.println(error.status() + " " + error.code() + " " + error.requestId());} catch (NetworkException error) {    System.err.println(error.isTimeout() ? "Timed out" : "No response");}

A refusal from the API throws an ApiException, or the subclass for its type, with what the API said about it. Every exception the client throws is an unchecked OpenEmailException, so one catch covers them all.

MethodWhat it holds
status()The HTTP status, or 0 when no response arrived.
type(), code()The error type and the code of the API, such as validation_error and invalid_parameter.
param()The field that is to blame, when there is one.
requestId()The id to quote when you write to support.
docUrl()The page of the docs that explains the code.
retryAfterSeconds()How long the API asked you to wait.
fields()The fields a sign-up form refused, each with a key and an error.
body()The decoded response body.

The kinds of failure

ExceptionWhen it is thrown
AuthenticationExceptionThe key or token was refused, with a 401.
PermissionExceptionThe credential may not do this, with a 403.
NotFoundExceptionNothing has that id, with a 404.
ConflictExceptionThe change conflicts with the current state, with a 409.
ValidationExceptionA field was refused, with a 422.
RateLimitExceptionToo many requests, or an allowance is spent, with a 429.
InvalidRequestExceptionAny other refusal of the request.
ApiExceptionThe API failed, with a status of 500 or more.
NetworkExceptionNo response arrived.
IllegalArgumentExceptionThe call itself was wrong, and nothing was sent.
WebhookSignatureExceptionA webhook delivery failed its check.

An ApiException also answers questions about itself: isValidation(), isNotFound(), isRateLimited(), isServerError() and isRetryable(), with isScopeMissing() when the credential lacks the scope the call needs and isStepUpRequired() when an access token has to verify a code first. NetworkException.isTimeout() says the deadline passed.

What is tried again

  • Reads, sends and every write that is safe to repeat are tried again, up to maxRetries times. Any other write is sent once.
  • The statuses 408, 500, 502, 503 and 504 are retried with a backoff that starts at half a second and doubles up to eight seconds.
  • A 429 is retried only when it carries a Retry-After of a minute or less, and the client waits that long.
  • A connection that fails or times out is retried the same way.

A send that is retried carries the same idempotency key every time, so the API replays the first message instead of sending a second one.