Poison.EncodeError when incoming post request fails

I’ve been seeing this error in the logs recently but I am not sure what it is being caused by:

[error] #PID<0.2222.0> running Service.Web.Endpoint terminated
Server: service.com:443 (https)
Request: POST /webhook
** (exit) an exception was raised:
  ** (Poison.EncodeError) unable to encode value: {:safe, ["" | "<p>500 — internal server error</p>\n"]}
    (poison) lib/poison/encoder.ex:354: Poison.Encoder.Any.encode/2
    (poison) lib/poison.ex:41: Poison.encode!/2
    (phoenix) lib/phoenix/controller.ex:642: Phoenix.Controller.do_render/4
    (phoenix) lib/phoenix/endpoint/render_errors.ex:58: Phoenix.Endpoint.RenderErrors.__catch__/5
    (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
    (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

I suppose I should somehow add “500.json” template or render clause to the ErrorView, but I am not sure. My current view looks like this

defmodule Service.Web.ErrorView do
  use Service.Web, :view

  def render("404.html", _assigns) do
    render "not_found.html", %{}
  end

  def render("500.html", _assigns) do
    render "internal.html", %{}
  end

  # 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.html", assigns
  end
end

Is this a symptom of a bigger problem in my code since it wants to render 500 page for some reason? Would a longer stack-trace help? What is the usual way of dealing with problems like this (when the cause of the problem is not readily apparent) when they appear on a “production” machine?

Since poison is involved as well as a :safe-tuple, it seems as if you render some *.html but then you try to encode that as JSON.

And poison has no clue how to do that, since there is no replacement for a tuple in JSON as well as you can’t convert improper lists to JSON.

Therefore I think, there is happening something weird in your corresponding action.


edit

I have just seen, that you are rendering “500.html” as the default fallback. Please try to filter on the requested format like this:

def template_not_found(template, assigns) do
  render "500#{Path.extname template}", assigns
end

Also make sure you do have a corresponding template for all fileexetensions that are somehow generated dynamically and thus could produce a 500y error.

2 Likes

Thanks. That and adding two additional render clauses for 404.json and 500.json solved the issue with Poison. And the problem that caused 500.html being rendered became apparent in the new stack-trace.