# Trapping exits so terminate/2 actually runs

A GenServer that flushes buffered work in `terminate/2` loses it when its supervisor shuts it down, unless the process traps exits:

```elixir
defmodule EventBuffer do
  use GenServer

  def start_link(opts), do: GenServer.start_link(__MODULE__, opts, name: __MODULE__)

  @impl GenServer
  def init(_opts) do
    Process.flag(:trap_exit, true)
    {:ok, []}
  end

  @impl GenServer
  def terminate(reason, events) do
    IO.puts("flushing #{length(events)} events (#{inspect(reason)})")
  end
end
```

Stopping the supervisor with two events in the buffer:

```elixir
Supervisor.stop(sup)
# without trap_exit: nothing
# with trap_exit:    flushing 2 events (:shutdown)
```

The supervisor stops a child with a `:shutdown` exit signal. A process that isn't trapping exits dies from that signal immediately, so `terminate/2` never runs. With `trap_exit`, the signal becomes a message, and `GenServer` calls `terminate/2` before exiting.

Trapping exits also turns a crash in any linked process into an `{:EXIT, pid, reason}` message instead of taking the server down. Without a matching `handle_info/2`, that only shows up as an "unexpected message" log. And `terminate/2` still has to finish within the child's `:shutdown` timeout, 5000 ms for workers by default, or the supervisor kills it anyway.

https://hexdocs.pm/elixir/GenServer.html#c:terminate/2


---

Created by: almirsarajcic
Date: September 28, 2026
URL: https://elixirdrops.net/d/ZFDbvCtx
