Phoenix.NotAcceptableError - How can I return an error instead of having the app just have an exception?

HTTP request error: no supported media type in accept header.

Expected one of ["html"] but got the following formats:

  * "application/json;odata.metadata=minimal" with extensions: ["json"]

To accept custom formats, register them under the `:mime` library
in your config/config.exs file:

    config :mime, :types, %{
      "application/xml" => ["xml"]
    }

And then run `mix deps.clean --build mime` to force it to be recompiled.

Right now AppSignal is triggering an error with this. How would guys recommend I handle this and only accept html requests for the root path?

Appreciate the guidance!

2 Likes

There is a way to specify http status code and plug will intercept the exception and show the error message with the specified status code or something like that. That’s how ecto exceptions work in phoenix.

So, you either need to define an implementation for the exception

defimpl Plug.Exception, for: Your.Error do
  def status(_exception), do: 400 # or anything else
end

or have a :plug_status field in the exception.

defmodule Your.Error do
  defexception [message: "...", plug_status: 400]
end

More on this: http://joshwlewis.com/essays/elixir-error-handling-with-plug/

4 Likes

I wonder if this answer no longer applies to more recent versions of Phoenix. When I add an implementation like the above for Phoenix.NotAcceptableError:

defimpl Plug.Exception, for: Phoenix.NotAcceptableError do
  def status(_exception), do: 404
end

I get a warning warning: function actions/1 required by protocol Plug.Exception is not implemented (in module Plug.Exception.Phoenix.NotAcceptableError). So I’ve added that (returning an empty map since I don’t have any custom actions to add):

# lib/my_app_web/plug_exception_implementations.ex
defimpl Plug.Exception, for: Phoenix.NotAcceptableError do
  def status(_exception), do: 404

  def actions(_exception) do
    %{}
  end
end

but it appears to have no effect. A controller test like

test "rescues Phoenix.NotAcceptableError", %{conn: conn} do
  conn =
    conn
    |> put_req_header("accept", "text/plain")
    |> get("/ads.txt")
  
  assert html_response(conn, 404)
end

still fails with raising ** (Phoenix.NotAcceptableError) no supported media type in accept header. instead of returning a 404 status. :thinking:

1 Like

The Plug.Exception protocol did change. The actions/1 callback should return a list, though. Here’s the minimal implementation:

defimpl Plug.Exception, for: Phoenix.NotAcceptableError do
  def status(_exception), do: 400
  def actions(_exception), do: []
end

To verify this in tests, you need to call assert_error_set/2.

Also note that the response body depends on the value of the debug_errors flag in your config.

1 Like