GenServer start_link when stopped in init no longer returns a value in OTP 26?

I need advice as to whether this is a bug in OTP 26 (and where to report it?), or an expected change (is it listed in Otp 26.0 - Erlang/OTP ?)

I have a GenServer that can be stopped in its init callback like this:

defmodule StoppingServer do
  use GenServer

  @spec start_link(keyword()) :: {:ok, pid()} | {:error, atom()}
  def start_link(init_arg) do
    GenServer.start_link(__MODULE__, init_arg)
  end

  def init(_) do
    {:stop, :you_cant_start_me}
  end
end

When I call StoppingServer.start_link([]) with Elixir 1.15 or 1.14 and OTP 25, the call returns a value and then the process exits:

iex()> StoppingServer.start_link([])
{:error, :you_cant_start_me}
** (EXIT from #PID<0.108.0>) shell process exited with reason: :you_cant_start_me

However, when I use OTP 26, the process first exits. If I want to get the return value, I need to trap exits.

iex()> StoppingServer.start_link([])
** (EXIT from #PID<0.113.0>) shell process exited with reason: :you_cant_start_me

Interactive Elixir (1.15.0) - press Ctrl+C to exit (type h() ENTER for help)
iex()> Process.flag(:trap_exit, true)
false
iex()> StoppingServer.start_link([])
{:error, :you_cant_start_me}

Is that expected now? Does the Elixir documentation about GenServer need to be updated? The wording there suggests to me that the value will be returned first:

Returning {:stop, reason} will cause start_link/3 to return {:error, reason} and the process to exit with reason reason without entering the loop or calling terminate/2.

2 Likes

Seems like it was an intentional change in OTP 26.

4 Likes

Maybe related, I upgraded my server from OTP24 to 26, and didn’t change other code. Then I got a lot error log like:

[error] GenServer #PID<0.397996.0> terminating\n** (stop) :sock_closed\nLast message: {:tcp_closed, #Port<0.10401>}

Have no idea where the error from, because all the GenServers we using were registered with a name.

Sorry, I found out my errors is caused by Add OTP-26 to CI by seriyps · Pull Request #284 · epgsql/epgsql · GitHub , not related with your topic.

Is this new behavior documented somewhere? Just noticed this today and I was pretty surprised by it. Up to Elixir 1.16 the docs say:

Returning {:stop, reason} will cause start_link/3 to return {:error, reason} and the process to exit with reason reason without entering the loop or calling terminate/2.

But you can’t have the proper value back unless you call Process.flag(:trap_exit, true).