Skip to content

Decide on format for error handling #123

Closed

Description

@niklaslong

As noticed in #120, we currently have different shapes for the errors in the application:

  • {:error, message, detail}
  • {:error, %{message: message, detail: detail}

This issue is meant to start the discussion around how we structure errors, abstractions on error handling and so on...

Activity

  1. jinjagit commented on Sep 7, 2020

    @jinjagit
    Member

    I based error messages on the format I found in assert details == %{email: ["is invalid"]} from test/liquid_voting_web/absinthe/mutations/create_participant_test.exs
    I'm not sure, but that error looks like it is coming form an ecto email validation, via a changeset, which confuses me if it is not in the format we desire.
    The structure used for matching in that test is {:ok, %{errors: [%{message: message, details: details}]}} = ...
    But I am also beginning to see how having different error formats can cause pattern-matching difficulties.

  2. jinjagit commented on Nov 11, 2020

    @jinjagit
    Member

    Useful / relevant posts & articles:

    1. https://elixirforum.com/t/best-practises-for-error-handling-elixir/2532/5 - especially this post about error pattern matching
    2. https://shyr.io/blog/absinthe-exception-error-handling - Includes an example error handling module with custom error struct
  3. niklaslong commented on Nov 11, 2020

    @niklaslong
    CollaboratorAuthor

    Random thought: might be worth creating an Error struct(s)? I'm mentioning it here as it's actually something I've struggled with in other projects in the past as well, would be curious to hear thoughts on this. E.g.:

    {:error, %Error{message: "ran out of chocolate", detail: "there's never enough chocolate", ...}}
    

    Edit: conceivably could be matched on type as well (not sure if this is a good idea or an Elixir antipattern):

    {:error, %SomeError{}}} -> ...
    {:error, %SomeOtherError{}} -> ...
    
  4. jinjagit commented on Nov 11, 2020

    @jinjagit
    Member

    So, here's my current idea:

    • Create an error handling module. All functions that expect an {:ok, something} also match for {:error, some_error_stuff} which is then passed on to this error handling module by bubbling the error up (as just {:error, some_error_stuff} to the highest level involved in the call, which then passes the error on to the error handling module.
    • This error handling module can then match against all needed error formats. Typically, this would be message: "message_string", details: "details_string" (or an error struct, as suggested by Niklas).
    • If, for example, the format details: %{email: ["is invalid"]} is returned by the phoenix email validation process, then we can add a match for this format in the error handling module, and then decode this into our standard format (e.g. details: "email is invalid")
    • This way, we only have to add any 'new / different' error formats to one place, all functions that finally return :ok or :error just match {:error, some_error_stuff} -> error_handler(some_error_stuff).
    • Niklas's error struct idea seems like a good one, and could be incorporated into this approach.
    • The final error handling module pattern match could be a catch-all that simply turns whatever the (unexpectedly weird) error format is into a string, so that at least we will see the error as a return value in absinthe and hopefully think "We need to change that error format / explicitly parse it in the error handling module"
  5. jinjagit commented on Nov 11, 2020

    @jinjagit
    Member

    TLDR: Basically, I think we need to handle cases where the error format does not match our chosen format and we do not control the format that is given to us (as in the invalid email example, above). This handling needs to be centralized and clear.

  6. locked and limited conversation to collaborators on Mar 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions