Description
Errors is an Elixir package that adds debugging context to error reasons. It is meant to be used in the tagged tuple style of error handling, where a function may return {:ok, result} or {:error, reason}.
errors alternatives and similar packages
Based on the "Errors and Exception Handling" category.
Alternatively, view errors alternatives based on common mentions on social networks and blogs.
-
elixir_error_message
Elixir error messages to make the errors within the system easier to expect and predict, as well as read
Stream - Scalable APIs for Chat, Feeds, Moderation, & Video.

Do you think we are missing an alternative of errors or a related project?
README
Errors is an Elixir package that adds debugging context to error reasons. It is meant to be used in the tagged tuple style of error handling, where a function may return {:ok, result}
or {:error, reason}
.
Motivation
To illustrate why this might be useful, consider the following code snippet that fetches the contents of a file from a GitHub repo:
defmodule HTTP do
def get(url) do
HTTPoison.get(url)
end
end
defmodule GitHub do
def file({user, repo, file}) do
HTTP.get("https://raw.githubusercontent.com/#{user}/#{repo}/master/#{file}")
end
end
defmodule Repo do
def read(path) do
GitHub.file({"nucleartide", "errors", path})
end
end
# iex> Repo.read("notafile.txt")
# ...> {:error, %HTTPoison.Error{id: nil, reason: :closed}}
Here, we see that the Repo.read/1
operation failed, but the error reason isn't particularly helpful. Where does the HTTPoison.Error
come from? What was :closed
?
This error is even more opaque if Repo.read/1
comes from a third-party library.
Solution
Errors clarifies error reasons by annotating reasons with a message and stack trace. Here's how you would refactor the code above to provide additional debugging context:
defmodule HTTP do
def get(url) do
with res = {:ok, _} <- HTTPoison.get(url) do
res
else
{:error, e} -> {:error, Errors.wrap(e, "http request failed")}
end
end
end
defmodule GitHub do
defp url({user, repo, file}) do
"https://raw.githubusercontent.com/#{user}/#{repo}/master/#{file}"
end
def file(github_file) do
with res = {:ok, _} <- HTTP.get(github_file |> url()) do
res
else
{:error, e} -> {:error, Errors.wrap(e, "couldn't fetch github file")}
end
end
end
defmodule Repo do
def read(path) do
with res = {:ok, _} <- GitHub.file({"nucleartide", "errors", path}) do
res
else
{:error, e} -> {:error, Errors.wrap(e, "couldn't read from #{path}")}
end
end
end
# iex> {:error, err} = Repo.read("notafile.txt")
# ...> {:error, %Errors.WrappedError{...}}
We can print this new Errors.WrappedError
to retrieve our annotated messages:
iex> IO.puts(err)
couldn't read from notafile.txt: couldn't fetch github file: http request failed: closed
Or inspect the error for a stack trace:
iex> IO.inspect(err)
** (Errors.WrappedError) couldn't read from notafile.txt
example.exs:27: HTTP.get/1
** (Errors.WrappedError) couldn't fetch github file
example.exs:17: GitHub.file/1
** (Errors.WrappedError) http request failed
example.exs:6: Repo.read/1
** (HTTPoison.Error) closed
Install
Add :errors
to your deps
in mix.exs
:
def deps do
[
{:errors, "~> 0.1.0"}
]
end
API
Errors exposes three primary macros/functions:
Errors.wrap/2
@spec wrap(error :: Exception.t, message :: String.t) :: Macro.t
defmacro wrap(error, message \\ "")
Use Errors.wrap/2
to "wrap" tagged errors received from other functions:
defmodule YourModule do
require Errors
def get() do
with res = {:ok, _} <- HTTPoison.get("https://www.google.com/") do
res
else
{:error, e} -> {:error, Errors.wrap(e, "can't ping google")}
end
end
end
Errors.new/1
@spec new(message :: String.t) :: Macro.t
defmacro new(message \\ "")
Use Errors.new/1
as a general replacement for custom Exception structs:
defmodule YourModule do
require Errors
def transform("https://" <> rest_of_link),
do: {:ok, rest_of_link}
def transform(_),
do: {:error, Errors.new("missing https:// prefix")}
end
Errors.cause/1
@spec cause(error :: Errors.Cause.t | any) :: Exception.t | any
def cause(error)
Errors.cause/1
returns the unwrapped "cause" of an error.
The passed-in error
should implement the Errors.Cause
protocol; errors returned by Errors.wrap/2
and Errors.new/1
implement Errors.Cause
already:
require Errors
%RuntimeError{message: "this is an error"}
|> Errors.wrap("uh-oh")
|> Errors.cause() # => %RuntimeError{message: "this is an error"}
WrappedError
Errors.wrap/2
and Errors.new/1
return a WrappedError
. WrappedError
implements the String.Chars and Inspect protocols, so you can freely print and inspect:
e = %RuntimeError{} |> Errors.wrap("uh-oh")
IO.puts(e) # will print "uh-oh: runtime error"
IO.inspect(e) # will print a stack trace
Hexdocs
See the hexdocs for more details and links to source code.
Feedback, issues, concerns
Please open an issue!
Links
Jason Tu · GitHub @nucleartide · Twitter @nucleartide