The error returned by every Zizq function.
Every failure arrives as {:error, %Zizq.Error{}} — a request the
server rejected, a server that could not be reached, a body that
would not encode — so there is a single shape to match on wherever
the problem occurred. The :reason field says which kind it was.
case Zizq.enqueue(job, MyApp.Zizq) do
{:ok, job} ->
job
{:error, %Zizq.Error{reason: :forbidden}} ->
needs_a_pro_license()
{:error, %Zizq.Error{} = error} ->
Logger.error(Exception.message(error))
endReasons are atoms, so a guard covers a whole class of failure when the specific one does not matter:
{:error, %Zizq.Error{reason: reason}} when reason in [:not_found, :conflict] ->
:already_handledFields
:reason— the kind of failure; seereason/0:message— human-readable, preferring the server's own wording:status— the HTTP status, ornilwhen no response arrived:body— the decoded response body, when there was one:cause— the underlying exception behind a transport or codec failure, so the original detail is still available
Deciding whether to retry
Use retryable?/1 rather than inspecting the status yourself. It
keeps the policy in one place, and it is the same policy the worker
applies to acknowledgements: transport failures and 5xx are
transient, everything else is permanent.
Summary
Functions
Wrap a response body that could not be deserialised.
Wrap a request body that could not be serialised.
Build an error from a non-success HTTP response.
Whether retrying the same request could plausibly succeed.
Wrap a transport-level failure.
Types
@type reason() ::
:not_found
| :conflict
| :forbidden
| :unsupported_format
| :invalid_request
| :client_error
| :server_error
| :transport
| :encode
| :decode
| :unexpected_status
Why the call failed.
:not_found— 404. A job or cron entry that does not exist.:conflict— 409.:forbidden— 403. The server refused a licensed feature; see the message for which one.:unsupported_format— 406 or 415. Content negotiation failed, which indicates a client bug rather than bad input.:invalid_request— 400 or 422. The server rejected the request.:client_error— any other 4xx.:server_error— any 5xx.:transport— the request never completed: connection refused, timeout, DNS failure, pool unavailable.:encode— the request body could not be serialised.:decode— the response body could not be deserialised.:unexpected_status— the server answered with a status this endpoint does not know how to interpret. A client bug, or a server newer than this client.
@type t() :: %Zizq.Error{ __exception__: term(), body: term(), cause: Exception.t() | nil, message: String.t(), reason: reason(), status: non_neg_integer() | nil }
Functions
@spec decode(Exception.t()) :: t()
Wrap a response body that could not be deserialised.
@spec encode(Exception.t()) :: t()
Wrap a request body that could not be serialised.
@spec from_response(non_neg_integer(), term()) :: t()
Build an error from a non-success HTTP response.
Callers decide which statuses count as failures; the transport layer
passes every status through untouched. That matters because status
alone is not always enough: a 422 from the bulk acknowledge endpoint
reports partial success ({"not_found" => [...]}, the rest having
completed), where a 422 elsewhere is a genuine rejection.
Whether retrying the same request could plausibly succeed.
True for :transport and :server_error; false for everything else.
A 4xx will fail identically however many times it is sent, and a
body that would not encode will not encode on a second attempt.
iex> Zizq.Error.retryable?(%Zizq.Error{reason: :server_error})
true
iex> Zizq.Error.retryable?(%Zizq.Error{reason: :not_found})
false
@spec transport(Exception.t()) :: t()
Wrap a transport-level failure.