Skip to the documentation
Go

Errors and retries

One error type for every failure, a sentinel for every kind, and retries that cannot send twice.

Handling an error

errors.go
_, err := client.Emails.Send(ctx, openemail.Body{	"from":    "[email protected]",	"to":      "[email protected]",	"subject": "Your September invoice",	"text":    "Invoice attached.",}) var failure *openemail.Error switch {case err == nil:	fmt.Println("sent")case errors.Is(err, openemail.ErrValidation):	errors.As(err, &failure)	fmt.Println(failure.Code, failure.Param, failure.Message)case errors.Is(err, openemail.ErrRateLimited):	errors.As(err, &failure)	fmt.Println("try again in", failure.RetryAfter)case errors.Is(err, openemail.ErrTimeout):	fmt.Println("no answer in time")default:	return err}

Every failure is an *openemail.Error. errors.Is tells which kind it is, and errors.As gives the error itself, with what the API said about it.

FieldWhat it holds
StatusThe HTTP status, or 0 when no response arrived.
Type, CodeThe error type and the code of the API, such as validation_error and invalid_parameter.
ParamThe field that is to blame, when there is one.
RequestIDThe id to quote when you write to support.
DocURLThe page of the docs that explains the code.
RetryAfterHow long the API asked you to wait.
FieldsThe fields a sign-up form refused, each with a Key and an Error.
BodyThe decoded response body.

The kinds of failure

SentinelWhen it matches
ErrAuthenticationThe key or token was refused, with a 401.
ErrPermissionThe credential may not do this, with a 403.
ErrScopeMissingThe credential lacks the scope the call needs.
ErrStepUpRequiredAn access token has to verify a code first.
ErrNotFoundNothing has that id, with a 404.
ErrConflictThe change conflicts with the current state, with a 409.
ErrValidationA field was refused, with a 422.
ErrRateLimitedToo many requests, or an allowance is spent, with a 429.
ErrInvalidRequestAny other refusal of the request.
ErrServerThe API failed, with a status of 500 or more.
ErrRetryableThe status is one the client retries.
ErrNetworkNo response arrived.
ErrTimeoutNo response arrived before the timeout.
ErrInvalidArgumentThe call itself was wrong, and nothing was sent.
ErrWebhookSignatureA webhook delivery failed its check.

A cancelled context still matches context.Canceled, and a deadline that passed matches context.DeadlineExceeded.

What is tried again

  • Reads, sends and every write that is safe to repeat are tried again, up to WithMaxRetries 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.