ErrorView and undefined function translate_error/1

I’ve got errors_view.ex in Phoenix, trying to render some JSON error when the changeset doesn’t pass while creating a record on a table.

defmodule BloodbathWeb.ErrorView do
  use BloodbathWeb, :view

  def translate_errors(changeset) do
    Ecto.Changeset.traverse_errors(changeset, &translate_error/1)

  def render("error.json", %{changeset: changeset}) do
    %{errors: translate_errors(changeset)}

  def render("400.json", _assigns) do
    %{errors: ["Bad request"]}

  def render("401.json", _assigns) do
    %{errors: ["Unauthorized"]}

  def render("404.json", _assigns) do
    %{errors: ["Page not found"]}

  def render("500.json", _assigns) do
    %{errors: ["Internal server error"]}

  # In case no render clause matches or no
  # template is found, let's render it as 500
  def template_not_found(_template, assigns) do
    render "500.json", assigns

I’ve seen this traverse_error multiple times searching on Internet, including in this forum, but when trying to boot it it throws this

== Compilation error in file lib/bloodbath_web/rest/views/error_view.ex ==
** (CompileError) lib/bloodbath_web/rest/views/error_view.ex:9: undefined function translate_error/1
    (elixir 1.11.4) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib 3.14) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir 1.11.4) lib/kernel/parallel_compiler.ex:314: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

What am I doing wrong here?

The dreaded missing s :scream_cat:

1 Like

@al2o3cr Thanks for your answer, but I think that’s not it, I’ve seen this example in lots of places including this forum and it was always with a difference between translate_errors and translate_error I’m not really sure why or/and where it comes from … For example here

Anyway my work around was to do this

  def render_with({message, values}) do
    Enum.reduce values, message, fn {k, v}, acc ->
      String.replace(acc, "%{#{k}}", to_string(v))

  def render_with(message) do

  def render("error.json", %{changeset: changeset}) do
    errors =, fn {field, detail} ->
        "#{field}": render_with(detail)

    %{errors: errors}

Which’s basically just traversing the changeset errors by myself, I’m not 100% satisfied but it works so far in my case.

Thanks for taking the time anyway :smile:

In my (fairly default) Phoenix app, the translate_error/1 function is defined in MyAppWeb.ErrorHelpers (located in the views directory). That module is imported into every view, see the view function in MyAppWeb.

For the sake of completeness, this is my (default generated) ErrorHelpers module:

defmodule MyAppWeb.ErrorHelpers do
  @moduledoc """
  Conveniences for translating and building error messages.

  use Phoenix.HTML

  @doc """
  Generates tag for inlined form input errors.
  def error_tag(form, field) do, field), fn error ->
      content_tag(:span, translate_error(error),
        class: "invalid-feedback",
        phx_feedback_for: input_id(form, field)

  @doc """
  Translates an error message using gettext.
  def translate_error({msg, opts}) do
    # When using gettext, we typically pass the strings we want
    # to translate as a static argument:
    #     # Translate "is invalid" in the "errors" domain
    #     dgettext("errors", "is invalid")
    #     # Translate the number of files with plural rules
    #     dngettext("errors", "1 file", "%{count} files", count)
    # Because the error messages we show in our forms and APIs
    # are defined inside Ecto, we need to translate them dynamically.
    # This requires us to call the Gettext module passing our gettext
    # backend as first argument.
    # Note we use the "errors" domain, which means translations
    # should be written to the errors.po file. The :count option is
    # set by Ecto and indicates we should also apply plural rules.
    if count = opts[:count] do
      Gettext.dngettext(MyAppWeb.Gettext, "errors", msg, msg, count, opts)
      Gettext.dgettext(MyAppWeb.Gettext, "errors", msg, opts)