{"id":33901124,"url":"https://github.com/cheerfulstoic/triage","last_synced_at":"2026-03-17T22:10:03.907Z","repository":{"id":319089254,"uuid":"1072325606","full_name":"cheerfulstoic/triage","owner":"cheerfulstoic","description":"Making dealing with results (ok/error) easy","archived":false,"fork":false,"pushed_at":"2026-02-10T09:06:28.000Z","size":272,"stargazers_count":1,"open_issues_count":9,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-10T14:59:04.165Z","etag":null,"topics":["elixir","errors","results"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/triage","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cheerfulstoic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-08T15:06:13.000Z","updated_at":"2026-02-10T09:06:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"92c100e4-86a2-41fb-a097-718aac969998","html_url":"https://github.com/cheerfulstoic/triage","commit_stats":null,"previous_names":["cheerfulstoic/errors","cheerfulstoic/triage"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cheerfulstoic/triage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheerfulstoic%2Ftriage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheerfulstoic%2Ftriage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheerfulstoic%2Ftriage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheerfulstoic%2Ftriage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cheerfulstoic","download_url":"https://codeload.github.com/cheerfulstoic/triage/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheerfulstoic%2Ftriage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30208730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T03:24:23.086Z","status":"ssl_error","status_checked_at":"2026-03-07T03:23:11.444Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","errors","results"],"created_at":"2025-12-11T23:49:17.428Z","updated_at":"2026-03-17T22:10:03.876Z","avatar_url":"https://github.com/cheerfulstoic.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"**IMPORTANT NOTE**: This library's API and documentation is very fleshed out. But much of this library is a work-in-progress conceptually. Please consider everything as potentially open to change!\n\n# Triage\n\nA lightweight Elixir library for enhanced handling of **results** (`{:ok, _}` / `:ok` / `{:error, _}` / `:error`) with context wrapping, logging, and user message generation.\n\n## Features\n\nThis package provides three levels of working with errors which are all **usable independently**, but which all complement each other.\n\n- **Context Wrapping**: Add meaningful context to errors as they bubble up through your application\n- **Result Logging**: Log errors (and optionally successes) with file/line information\n- **User-friendly errors**: Be able to collapse errors into a single user error message\n- **Error enumeration**: functions like `map_if`, `find_value`, and `all` help deal with enumerations over data where each iteration may succeed or fail.\n- **Error control flow**: `ok_then` and `error_then` functions help control and transform results\n\nDesign goals:\n\n- Standard results (`:ok`, `:error`, `{:ok, term()}`, `{:error, term()}` with only one value in tuples)\n- Avoid macros for easy pick-up-and-use throughout a codebase (i.e. no need for `require`)\n- Variety of small tools which work well together (like UNIX commands)\n\nSee the [Philosophy](https://hexdocs.pm/triage/philosophy.html) section of the docs for more details.\n\n## Examples\n\n### Contexts\n\nWhen an error is returned (e.g. in a tuple, as opposed to being raised), often that error can be passed up a stack and it becomes unclear where the error came from. Triage offers a `wrap_context` function to attach a context string and/or metadata to errors via a `WrappedError` exception struct.\n\n```elixir\ndefmodule MyApp.OrderProcessor do\n  def process_payment(order) do\n    with {:ok, payment_method} \u003c- fetch_payment_method(order),\n         {:ok, charge} \u003c- charge_payment(payment_method, order.amount) do\n      {:ok, charge}\n    end\n    |\u003e Triage.wrap_context(\"process payment\", %{order_id: order.id, order_amount: order.amount})\n  end\n  # ...\nend\n\ndefmodule MyApp.OrderService do\n  def complete_order(order_id) do\n    fetch_order(order_id)\n    |\u003e MyApp.OrderProcessor.process_payment()\n    |\u003e Triage.wrap_context(\"complete order\")\n  end\n  # ...\nend\n```\n\nBut an error wrapped with a context isn't so useful by itself.  Your code can look at the `WrappedError` if you'd like, but it can be most useful with the output tools below.\n\n(Also, make sure to see the [Contexts section of the docs](https://hexdocs.pm/triage/contexts.html) for more information)\n\n### Output\n\nError results that you get back can be a mess. Often when you get an error tuple it comes back from a tree of nested calls and the reason value could be of many types (string, atom, etc...).  So it's useful to have tools which let you not worry about it so much. Below is an example of using `Triage.log` to output logs:\n\n```elixir\ndef show(conn, %{\"order_id\" =\u003e order_id}) do\n  order_id = String.to_integer(order_id)\n\n  MyApp.complete_order(order_id)\n  |\u003e Triage.log()\n  # ...\n```\n\nBy default `Triage.log` will only output error cases (pass in `:all` to log `:ok` results as well), so if this case is important we can have a log of how it went wrong. Also note that any metadata given to `log` is also assigned to the [Logger metadata](https://hexdocs.pm/logger/Logger.html#module-metadata) in addition to being outputted (helpful for filtering logs).\n\nThe output can be as simple as this in the case of an atom given as the error reason:\n\n```\n[RESULT] lib/my_app/order_controller.ex:41: {:error, :order_was_invalid}\n```\n\nBut if `Triage.wrap_context` is used, we can get even more details out:\n\n```\n[error] [RESULT] lib/my_app/order_service.ex:15: {:error, :payment_declined}\n  [CONTEXT] lib/my_app/order_service.ex:15: complete order\n  [CONTEXT] lib/my_app/order_processor.ex:8: process payment | %{order_id: 12345, amount: 99.99}\n```\n\nNote that if you'd prefer to output JSON logs, there is some [information in the docs](https://hexdocs.pm/triage/logging-json.html)\nAdditionally, the `Triage.user_message` function will extract a message from the error if possible.  If not possible, the user will be given a generic error with a randomly generated short code which can be matched to a log entry with details about the error.\n\n```elixir\ndef show(conn, %{\"order_id\" =\u003e order_id}) do\n  order_id = String.to_integer(order_id)\n\n  MyApp.complete_order(order_id)\n  |\u003e case do\n    {:ok, value} -\u003e\n      # ...\n\n    {:error, _} = error -\u003e\n      conn\n      |\u003e put_status(400)\n      |\u003e json(%{error: Triage.user_message(error)})\n  end\n  # ...\n```\n\nThe `user_message` function even supports a default implementation to provide `Ecto.Changeset` errors, so if it gets a changeset value in an error then users will get a reasonable value such as \"age: must be greater than 18; email: can't be blank\".\n\nSee the [Outputs section of the docs](https://hexdocs.pm/triage/outputs.html) for more information.\n\n### Enumeration\n\n`triage` has a set of functions to help when you have a series of step which might succeed or fail.  As an example, you may want to build up a list, but return an error if anything fails.\n\n```elixir\n  defp validate_each_metric(metrics, query) do\n    Enum.reduce_while(metrics, {:ok, []}, fn metric, {:ok, acc} -\u003e\n      case validate_metric(metric, query) do\n        {:ok, metric} -\u003e {:cont, {:ok, acc ++ [metric]}}\n        {:error, reason} -\u003e {:halt, {:error, reason}}\n      end\n    end)\n  end\n```\n\nThe `Triage.map_if` function is one tool available:\n\n```elixir\n  defp validate_each_metric(metrics, query) do\n    # Returns {:ok, [...]} where the original returned just [...]\n    Triage.map_if(metrics, \u0026 validate_metric(\u00261, query))\n  end\n```\n\nFor more functions and examples, see the [Enumerating Errors section of the docs](https://hexdocs.pm/triage/enumerating-errors.html).\n\n### Control Flow\n\n`triage`'s two control flow tools (`ok_then` and `error_then`) can both be shown via an HTTP request example:\n\n```elixir\nfetch_bill(bill_id)\n|\u003e Triage.ok_then(\u0026 HTTPoison.get(\u00261.pdf_url))\n|\u003e Triage.ok_then(fn\n  %HTTPoison.Response{status_code: 200, body: body} -\u003e\n    body\n\n  %HTTPoison.Response{status_code: 404, body: body} -\u003e\n    {:error, \"Server result not found\"}\nend)\n|\u003e Triage.error_then(fn\n    %HTTPoison.Error{reason: :nxdomain} -\u003e\n      \"Server domain not found\"\n\n    %HTTPoison.Error{reason: :econnrefused} -\u003e\n      \"Server connection refused\"\n\n    %HTTPoison.Error{reason: reason} -\u003e\n      \"Unexpected error connecting to server: #{inspect(reason)}\"\nend)\n```\n\nThe `Triage.ok_then` function works on `:ok` results, ignoring errors.  Values that are returned from the callback are automatically wrapped in an `{:ok, _}` tuple, though any `:error` or `{:error, term()}` returned will be returned as an error.\n\nThe `Triage.error_then` function is the opposite: working on `:error` reasons and returning new reasons to be wrapped in an `{:error, _}` tuple.  If an `:ok` or `{:ok, _}` result is returned, then the error is ignored and `Triage.error_then` will return that success.\n\nAdditionally there are `tap_ok` and `tap_error` function which allow you to execute side-effects (see also Elixir's [`Kernel.then/2`](https://hexdocs.pm/elixir/Kernel.html#then/2) and [`Kernel.tap/2`](https://hexdocs.pm/elixir/Kernel.html#tap/2) functions which are analogous to the above).\n\nMake sure to see the [Control Flow section of the docs](https://hexdocs.pm/triage/control-flow.html) for more information.\n\nAlso, many people wonder why they shouldn't just use `with` instead of `ok_then` / `error_then`.  There is a [section in the docs](https://hexdocs.pm/triage/comparison-to-with.html) for that too!\n\n## Installation\n\nAdd `triage` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:triage, \"~\u003e 0.5.0\"}\n  ]\nend\n```\n\nFor various reasons, `triage` requires at least version `1.15` of Elixir.\n\n## Usage\n\nSee [the docs](https://hexdocs.pm/triage) for detailed information about the different tools available.\n\n## Development\n\nRun tests:\n\nRun tests in watch mode (uses [`mix_test_interactive`](https://hex.pm/packages/mix_test_interactive):\n\n```bash\nmix test.interactive\n```\n\nOr just:\n\n```bash\nmix test\n```\n\n## License\n\nCopyright (c) 2025\n\nThis work is free. You can redistribute it and/or modify it under the\nterms of the MIT License. See the LICENSE file for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheerfulstoic%2Ftriage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcheerfulstoic%2Ftriage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheerfulstoic%2Ftriage/lists"}