Moov.Error exception (Moov v1.0.0)

Copy Markdown View Source

The error struct returned in the {:error, error} tuple from every Moov.* function, and the exception raised by !-suffixed variants.

Every field Moov could plausibly need to act on programmatically is surfaced directly on the struct instead of being buried in an opaque response map:

  • :type - a normalized, pattern-matchable atom (see type/0)
  • :status - the raw HTTP status code, if the error came from an HTTP response (nil for transport-level errors)
  • :message - a human-readable summary, suitable for logs
  • :body - the decoded JSON error body Moov returned, if any. For 422 responses this often contains field-level validation details
  • :request_id - the value of the x-request-id response header, handy when contacting Moov support about a specific failed call
  • :retry_after_ms - how long to wait before retrying, in milliseconds. Populated from a standard Retry-After header when present, or as a best-effort heuristic scanning the human-readable error message for a "... XXX ms ..." / "... XXX milliseconds ..." hint, since Moov's 429/422 responses describe the wait time in prose rather than a dedicated field (see https://docs.moov.io/api/errors/)
  • :reason - the raw underlying reason for transport-level errors (e.g. :timeout, :econnrefused)

Pattern matching on error type

case Moov.Accounts.get(client, account_id) do
  {:ok, account} ->
    account

  {:error, %Moov.Error{type: :not_found}} ->
    nil

  {:error, %Moov.Error{type: :too_many_requests, retry_after_ms: ms}} ->
    Process.sleep(ms || 1_000)
    Moov.Accounts.get(client, account_id)

  {:error, error} ->
    Logger.error("Moov error: " <> Exception.message(error))
    raise error
end

Summary

Functions

Builds a Moov.Error for a response body that could not be decoded as JSON.

Builds a Moov.Error from a raw HTTP response (status, headers, decoded body). Used internally by Moov.Client - you generally won't call this yourself, but it's public and pure so it's easy to unit test.

Builds a Moov.Error from a transport-level failure (DNS, TCP, TLS, timeout) where no HTTP response was ever received.

Types

t()

@type t() :: %Moov.Error{
  __exception__: true,
  body: map() | String.t() | nil,
  message: String.t(),
  reason: term(),
  request_id: String.t() | nil,
  retry_after_ms: non_neg_integer() | nil,
  status: pos_integer() | nil,
  type: type()
}

type()

@type type() ::
  :bad_request
  | :unauthorized
  | :forbidden
  | :not_found
  | :conflict
  | :unprocessable_entity
  | :too_many_requests
  | :server_error
  | :gateway_timeout
  | :network_error
  | :decode_error
  | :unknown

Functions

from_decode_error(reason, raw_body)

@spec from_decode_error(term(), String.t()) :: t()

Builds a Moov.Error for a response body that could not be decoded as JSON.

from_response(status, headers, body)

@spec from_response(pos_integer(), [{String.t(), String.t()}] | map(), term()) :: t()

Builds a Moov.Error from a raw HTTP response (status, headers, decoded body). Used internally by Moov.Client - you generally won't call this yourself, but it's public and pure so it's easy to unit test.

from_transport_error(reason)

@spec from_transport_error(term()) :: t()

Builds a Moov.Error from a transport-level failure (DNS, TCP, TLS, timeout) where no HTTP response was ever received.