An infinite loop in MyApp.ErrorView

I have this ErrorView:

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

  def render("404.json", _assigns) do
    json(%{error: "page not found"})
  end

  # infinite loop!!!
  def render("404.html", assigns) do
    render(__MODULE__, "404.html")
  end

  def render("500.json", assigns) do
    json(%{error: "internal server error"})
  end

  # infinite loop!!!
  def render("500.html", assigns) do
    render(__MODULE__, "404.html")
  end
end

The thing is that for html responses it enters an infinite loop when the 400 or 500 error occurs.

Why is that?

And what’s the exact signature of these functions supposed to be and how are they supposed to be called from a controller? I mean, should it be just def render("404", assigns)? Then this won’t be descriptive enough and also how would I distinguish between html and json and … whatever other format I have?

Take a very close look at the function head and the template in the render call. But how did you make it compile at all? According to the documentation there shouldn’t be a render/2 in a view, but only a render/3…

that’s not the exact code.

yes, render/3, but it’s not the point. the question is: why is infinite loop and how to fix it? this fixes it

def render("404_something.html", assigns) do
  render(__MODULE__, "404.html")
end

and this too:

def render("404.html", assigns) do
  render(__MODULE__, "404_something.html")
end

but why I can’t I give them the same name?

Just think about this as a lexicon:

Recursion

see “Recursion”

Won’t this be an infinite loop as well? And your code is exactly like this.

Then please show us the exact code.

This is one of the main points in programming: Beeing exact.

1 Like

you don’t need the exact code.

Well. I have already given an answer to your question as best as I am able to with the code you gave us. Also, it is hard to reason about a function that does not exist in the given context, so I had to assume, that it simply wraps Phoenix.View.render/3 with assigns beeing the empty list. But of course it could be, that you pull in render/2 from Phoenix.Controller, but that wouldn’t make much sense, since it requires a Plug.Conn as first argument, not an alias.

So my answer is based on assumptions.

If you gave the exact code that has the problem (and only that, the JSON related function heads are irrelevant to this problem), there were no need to assume anything.

@kaa.python, I believe you haven’t specified what you want to achieve.

I’m guessing here, but I assume what you want to achieve is to render a template named 404.html. If that is true, then what you need to do is to have a file in templates/error/404.html.eex and delete the render function on your ErrorView. You only need those functions if you don’t have any templates to render (therefore rendering a string or a json directly).

See the Phoenix guides for more info: The ErrorView.

Also, if I may give a suggestion, since you are asking for help/information, do help others to help you by giving the information they need. If you absolutely can’t give it, please give arguments and/or explanations on why you think the information you have given is enough. Simply saying “you don’t need them” or “that’s not the point” hardly helps. :slight_smile:

2 Likes
def render("404.html", assigns) do
  render(__MODULE__, "404.html")
end

The function body does call the function it’s in. If you want to call the template directly – and not the render function on the view module) – use render_template("foo.html", assigns). This is documented here: https://hexdocs.pm/phoenix/Phoenix.Template.html#module-rendering

2 Likes

Who among moderators keeps marking questions as solved when they shouldn’t?

Yeah, but in there it describes how to do that in a template, not in a view.

You can use render_template in a view too as the view calls use Phoenix.Template:

Also the paragraph below the code example in the docs does mention view modules as well.