# `WhatsApp.Error`
[🔗](https://github.com/jeffhuen/whatsapp_sdk/blob/main/lib/whatsapp/error.ex#L1)

WhatsApp API error.

Represents both API errors (4xx/5xx responses with Meta error bodies)
and connection-level errors (network failures, timeouts).

This module implements the `Exception` behaviour so errors can be raised
when desired, but are typically returned in `{:error, %WhatsApp.Error{}}`
tuples.

## Meta Error Format

Meta's Graph API returns errors in a consistent format:

    %{
      "error" => %{
        "message" => "Invalid OAuth access token...",
        "type" => "OAuthException",
        "code" => 190,
        "error_subcode" => 463,
        "fbtrace_id" => "AbC123dEf456",
        "is_transient" => false
      }
    }

## Usage

    case WhatsApp.Messages.send(client, params) do
      {:ok, message} ->
        message

      {:error, %WhatsApp.Error{code: 190}} ->
        Logger.error("Invalid access token")

      {:error, %WhatsApp.Error{status: 429, retry_after: seconds}} ->
        Process.sleep(seconds * 1_000)

      {:error, %WhatsApp.Error{} = err} ->
        Logger.error("WhatsApp error #{err.code}: #{err.message}")
    end

# `t`

```elixir
@type t() :: %WhatsApp.Error{
  __exception__: true,
  code: integer() | nil,
  details: map() | nil,
  error_subcode: integer() | nil,
  error_type: String.t() | nil,
  fbtrace_id: String.t() | nil,
  is_transient: boolean(),
  message: String.t(),
  retry_after: integer() | nil,
  status: integer() | nil
}
```

# `connection_error`

```elixir
@spec connection_error(Exception.t() | String.t()) :: t()
```

Build a connection-level error from a transport error or reason.

Used for network failures, timeouts, and other connection issues
where no HTTP response was received.

## Examples

    iex> error = WhatsApp.Error.connection_error(%Mint.TransportError{reason: :timeout})
    iex> error.message
    "timeout"

    iex> error = WhatsApp.Error.connection_error("connection refused")
    iex> error.message
    "connection refused"

# `from_response`

```elixir
@spec from_response(integer(), map()) :: t()
```

Build an error from an HTTP status code and a decoded response body.

Handles Meta's standard error format where the error details are nested
under an `"error"` key, as well as responses that don't follow this format.

## Examples

    iex> body = %{"error" => %{"message" => "Invalid token", "type" => "OAuthException", "code" => 190}}
    iex> error = WhatsApp.Error.from_response(401, body)
    iex> error.code
    190

# `from_response`

```elixir
@spec from_response(integer(), map(), list()) :: t()
```

Build an error from an HTTP status code, decoded response body, and response headers.

The headers list is inspected for a `Retry-After` header, which is parsed
as an integer number of seconds.

## Examples

    iex> body = %{"error" => %{"message" => "Rate limited", "code" => 80007}}
    iex> headers = [{"retry-after", "60"}]
    iex> error = WhatsApp.Error.from_response(429, body, headers)
    iex> error.retry_after
    60

# `message`

```elixir
@spec message(t()) :: String.t()
```

Returns the human-readable error message.

## Examples

    iex> WhatsApp.Error.message(%WhatsApp.Error{message: "Invalid token"})
    "Invalid token"

# `retryable?`

```elixir
@spec retryable?(t()) :: boolean()
```

Returns whether the error is retryable.

The following errors are considered retryable:

- Rate limit errors (HTTP 429)
- Server errors (HTTP 500, 502, 503)
- Connection errors (no HTTP status code)
- Errors explicitly marked as transient by Meta (`is_transient: true`)

## Examples

    iex> WhatsApp.Error.retryable?(%WhatsApp.Error{message: "rate limited", status: 429})
    true

    iex> WhatsApp.Error.retryable?(%WhatsApp.Error{message: "not found", status: 404})
    false

# `webhook_verification_error`

```elixir
@spec webhook_verification_error(String.t()) :: t()
```

Build an error for webhook verification failures.

## Examples

    iex> error = WhatsApp.Error.webhook_verification_error("token mismatch")
    iex> error.message
    "Webhook verification failed: token mismatch"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
