Search code examples
elixirphoenix-framework

Elixir Pheonix route unable to find appropriate module/function (module not avilable)


Using the default generated controller, I'm getting the following warning. I also received the warning when tinkering with adding another route. Clearly I'm doing something incorrectly, but it is not obvious from the error message.

warning: HelloWeb.HelloWeb.PageController.init/1 is undefined (module HelloWeb.HelloWeb.PageController is not available or is yet to be defined)
  lib/hello_web/router.ex:26: HelloWeb.Router.__checks__/0

My search results indicate there is generally a mismatch between the controller module name being used in the router, vs the one actually defined, but if that is the case I am unable to detect the difference.

Router (router.ex)
defmodule HelloWeb.Router do
  alias HelloWeb.PageController
  use HelloWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {HelloWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
  end

  scope "/", HelloWeb do
    pipe_through :browser

    # I should note that this line produces the following warning in VSCode using the Phoenix extension
    # "HelloWeb.HelloWeb.PageController.init/1 is undefined (module HelloWeb.HelloWeb.PageController is not available or is yet to be defined)"
    get "/", PageController, :home
  end
Controller (controllers/page_controller.ex)
defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def home(conn, _params) do
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :home, layout: false)
  end
end

What stands out to me here is that the warning refers to HelloWeb.HelloWeb.PageController though the module is named HelloWeb.PageController. I don't know the cause of the discrepancy, or even if it is related to the actual issue.

  • Why is this code generating a warning?
  • Why does the warning complain about the lack of an init/1 function?
  • Why is the module name in the warning longer than the module name defined in the controller file?

EDIT: Based on the helpful solution below, I discovered that the VSCode ElixirLS extension inserts aliases by default if required, which then breaks the way Phoenix handles routing. This can be disabled in the extension settings:

enter image description here

The downside is that disabling this seems to break autocomplete.


Solution

  • It has to do with the scoping of the router. When you do scope "/", HelloWeb, your router knows to look for controllers under the HelloWeb. prefix. So, you don't need to alias your controllers. By adding the alias, your router is duplicating the HelloWeb part of the module's name, as you can see on the warning.

    So:

    1. The warning is generated because the router is inferring the wrong name because of the alias that was added.
    2. init/1 is the function that gets called when spawning new processes, so your router is probably trying to spawn a module at HelloWeb.HelloWeb.PageController, but since that module doesn't exist with the double HelloWeb, it fails to find it and call its init/1 function.
    3. Just because of the combination of the way that module names are inferred and the presence of the alias.