DaveIdito
DaveIdito

Reputation: 1606

Phoenix/ Elixir Genserver won't start automatically. Error: shutdown: failed to start child

I have a GenServer in a module called CameraServer that fetches images which are then used by certain endpoints in the Phoenix Application.

defmodule MyApp.CameraServer do
  @name :image_server
  use GenServer

  def start_link(_opts) do
    IO.puts("Starting image server")
    GenServer.start(__MODULE__, [], name: @name)
    IO.puts("Image server started")
  end

  def get_images(_pid) do
    GenServer.call(@name, :clear)
  end

  def get_images(_pid, mac_address) do
    GenServer.call(@name, {:view, mac_address})
  end

  ## Genserver Server Code
  def init([]) do
    images = ["image_0", ...] 
    {:ok, images}
  end

  def handle_call(:clear, _sender, state) do
    {:reply, state, state}
  end

  def handle_call({:view, mac_address}, _sender, state) do
    results = Enum.filter(state, &String.contains?(&1, mac_address))
    {:reply, results, state}
  end
end

And my application.ex file is as follows.

defmodule MyApp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      MyApp.CameraServer,
      # Start the Ecto repository
      MyApp.Repo,
      # Start the Telemetry supervisor
      MyApp.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: MyApp.PubSub},
      # Start the Endpoint (http/https)
      MyApp.Endpoint
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ...
end

Now, when I run mix phx.server, I will get the error

** (Mix) Could not start application myapp: MyApp.Application.start(:normal, []) returned an error: shutdown: failed to start child: MyApp.CameraServer ** (EXIT) :ok

Strange thing is that I am able to see in the logs the outputs:

Starting image server

Image server started

Additionally, I can also run iex -S mix phx.server and then manually run MyApp.CameraServer.start_link([]) and then it works!

Upvotes: 0

Views: 435

Answers (1)

Aetherus
Aetherus

Reputation: 8898

I think the problem in the MyApp.CameraServer.start_link/1. You put IO.puts("Image server started") as the last statement, and it returns :ok, which is not a valid return value start_link/3 should return. It’s not a callback, hence it lacks an explicit contract, but implicitly it’d be added to the application supervision tree, which requires a proper return value to succeed.

You can change it to

  def start_link(_opts) do
    IO.puts("Starting image server")
    
    case GenServer.start_link(__MODULE__, [], name: @name) do
      {:ok, server} ->
        IO.puts("Image server started")
        {:ok, server}

      error ->
        IO.puts("Image server failed to start: #{inspect error}")
        error
    end
  end

By the way, I changed GenServer.start to GenServer.start_link, because you want your CameraServer process to link to its supervisor.

Upvotes: 3

Related Questions