verkhovin
Hi guys!
Want to open here another discussion about error handling concepts in Elixir because it looks like I really stuck here by myself.
My question is about errors that are related to users interactions, where you can’t just “Fail fast and forget / Let it crash” but need to respond with some appropriate error message.
Elixir community’s standard is to return tuples like {:error, reason} when something goes wrong during function execution. Overall, it looks good at the first sight.
But at some point, I noticed that to handle this approach I have to write tons of case or with really often just to check that underlying call didn’t return this {:error, reason} tuple.
Consider an example of a multilayered application (like Controller → Business Logic → database CRUDs). If there is some expected error that occurs on the layer of CRUDs, I will need to have a case to catch this error on a Business Logic layer and pass it to Controller. After that, I need to have another case on the Controller’s layer. And it becomes even worse if you have more than 3 layers there, or your business logic includes something more than just a call to CRUD layer.
Maybe I really missed something but it seems to me that this approach makes you write a lot of boilerplate.
I’m actually from Java world and it would be quite natural for me to resolve this issue by throwing (raising) an exception from the CRUDs level and catch in on the Controller level. But if I understand correctly, exceptions in Elixir philosophy are more about unexpected errors that should be handled by this “Let it crash” way.
My question still is: is it normal and common way to write elixir code having this error checks (case/with) on every layer of the application?
I can provide some naive code example if my thoughts are not clear.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #ai
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixirconf-us
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #security
- #performance










Most Liked- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
sezaru
I’m not sure if I follow you when you say you cannot redirect them all to the controller.
Let’s say you have a function
CRUD.getthat returns{:ok, value}or:error, reason}and that reason can be multiple types of errors.Now let’s say you want to redirect the errors to the controller from your business logic and handle the
:okcase, if you redirect something to the controller via this function return, you can simply do something like:This will handle the
:okcase and redirect all the other ones.If you need to call a function to redirect it to the controller, you can do something like:
or
In all these cases you don’t need to have a case for each error tuple, you can simply pattern match all of them.
Now at the Controller, if you want to handle each one of the errors, then you will need to use
caseor function pattern match to handle it, but that would be the same with java handling the catch cases.Doesn’t that solves the issue or am I missed the point entirely of what the real problem is in your case?
joaquinalcerro
You can also check this interesting post from @michalmuskala - Error Handling in Elixir Libraries | Michał Muskała
hauleth
I would say that it depends on the requirements and what kind of error is that:
Plug.Exceptiondo it’s thingLast Post!
amnu3387
I personally am very inclined to force explicitness and use more verbosity to describe flows. When using
withI normally resort to tagging the branches, but sometimes you do need to pass through the underlying returns from the failing branch. When writing API endpoints I’ve settled in just having a%Response{}/%Output{}struct with:errors,:notices,:payloadfields that the frontend knows how to interpret - this approach requires you to do it almost from the beginning and be in control also of how the front-end interacts. It’s also not completely feasible forhtmlpipelines, as you can have redirects and so on, but could probably be made so.Controllers/channels call only into context functions, and these entry points in the contexts either return those payload structs, or they return normal results and I have a translation module/functions for the returns. Again, it’s more boilerplaty.
But the issue that I think deserves a bit more thought is not that raising exceptions in themselves is bad (outside of things that really require raising, like a db disconnect, or inaccessibility of a resource, or inside a loop/recursion that no longer makes sense, but usually there you use
throw), when you look at the flow of a single entry point, it can usually look clearer and in a way less verbose. The problem comes when all your code base applies this style. Then it basically becomes a mine field because everything can raise somewhere and you’re left having to keep in mind every detail of all functions you’re calling and what those themselves are calling.When I have too much time to think about this nonsense I imagine I would like to see a variation of
with(a special form namedweaveorsteps) that would look like this:Perhaps it could even be that when the
doblock was ommitted it would pass the last value, without the keyword.This can be confusing the first time you encounter it (
not_authorized: true) but afterwards it’s simple to understand.Other option would be: