All posts

Fix Infinite Loop in Elixir

Troubleshoot and resolve infinite loops in Elixir, covering GenServer pitfalls, recursive processes, and message-passing deadlocks.

Infinite Loops in Elixir

Elixir processes are designed to loop — that's how GenServers work. But when a process enters an unintended infinite loop, it can spike CPU usage or fill the message queue until your BEAM VM runs out of memory.

Common Causes

  1. GenServer calling itself synchronously — A handle_call that sends a GenServer.call back to itself creates a deadlock.
  2. Missing base case in recursion — A recursive function that never matches the terminating clause.
  3. Infinite stream without a halt — Using Stream.iterate/2 without ever taking a finite slice.

Fixing It

The self-calling GenServer is especially sneaky:

# Bug: deadlock — handle_call calls itself
def handle_call(:compute, _from, state) do
  result = GenServer.call(__MODULE__, :fetch_data)
  {:reply, result, state}
end

# Fix: use a private function instead
def handle_call(:compute, _from, state) do
  result = fetch_data_internal(state)
  {:reply, result, state}
end

defp fetch_data_internal(state) do
  # direct logic, no GenServer.call to self
  Map.get(state, :data, :not_found)
end

For recursive functions, always pattern match a base case first:

def count_down(0), do: :done
def count_down(n) when n > 0, do: count_down(n - 1)

Monitoring in Production

Bugsly captures process crashes and timeouts in Elixir apps. When a GenServer times out after 5 seconds (the default), Bugsly logs the full call stack so you can trace where the loop began. Pair this with :observer.start() during local development to watch process message queues in real time.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free