Not an already existing atom` error fixed by arbitrary change in module. needs to be done after every `mix phx.server

I assume its a beam issue i have. I don’t know honestly.

Setup

  • my project contains fallowing module:
defmodule MyApp.Access do
  def permissions() do
    %{
      permission_x: [:list, :show],
      ...
    }
  end
end

In total project has 7 places where this atom permission_x is hardcoded.

Steps to consistently reproduce issue.

  1. open new terminal tab.
  2. ~ mix phx.server → server starts
  3. in browser i request 127.0.0.1:4000 → i get error. This error seems odd, as i have module where required atom is hardcoded, so how can it be not an already existing atom, but ok?
[error] #PID<0.766.0> running MayAppWeb.Endpoint (connection #PID<0.765.0>, stream id 1) terminated
Server: 127.0.0.1:4000 (http)
Request: GET /
** (exit) an exception was raised:
    ** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not an already existing atom

        :erlang.binary_to_existing_atom("permission_x", :utf8)
  1. I make arbitrary change to MyApp.Access module: (can’t add empty line, as on-save it is removed by formatter, so just added :ok)
defmodule MyApp.Access do
+ :ok
  def permissions() do
  1. in browser i request 127.0.0.1:4000 → and it works → terminal output:
Compiling 4 files (.ex)
[info] GET /
[info] Sent 200 in 262ms
  1. I can remove :ok line and it keeps working. → I assume compilation part is the secret sauce that fixes issue.

If i stop server ctrl+c and start again (step 2). im back in step 3. it does not work again. and i need to do some arbitrary changes to MyApp.Access to force compilation.

Is there a way to see what files are compiled there? Maybe there is some secret.
Also why it after restart loads old/stale binary of MyApp.Access not the latest one that worked?

I have spent a lot time to debug this and i dont understand what could even be next steps to try to fix this. :tired_face:

I assume compilation part is the secret sauce that fixes issue.

Yeah, compilation loads this module, therefore the atom is loaded.
Perhaps, you can try to ensure that the module is loaded, or just run any function from this module, because erlang loads modules on demand

The beam can start in one of two modes (configured through build_embedded iirc). Either it preloads all modules at the start, or it does load modules only on demand, when e.g. some code tries to call a function on it. By default in dev the beam runs in the second mode for the improved speed in startup. I’d expect that to be the reason for what you’re seeing

2 Likes

I’ve though so too, but I can see that

mix new sample
cd sample
iex -S mix

iex> String.to_existing_atom "world"

Works fine

hmm

1 Like

Maybe try something less common than the phrase world.

Yeah, I’ve tried other things and they worked correctly too

mix new sample
cd sample
iex -S mix

iex> String.to_existing_atom "world22222" <- results in error
iex> String.to_existing_atom "this_one" <- results in error

Please look at documentation for String.to_existing_atom/1 One have to define atom beforehand.

I can only assume your examples worked, becasue you used some generic words, that are already used registered by elixir it self.

I’ve used world and it was defined in lib/sample.ex

You can try to add any other atom into this module and it’ll be available in String.to_existing_atom

So, the issue is not with embedded mode or something like this

Or is it…

Atoms are loaded only for modules which are compiled during project initialization.

Anyway, I’d use Code.ensure_loaded for this.

I even created another module and tried to convert string to atom that was defined in an other module and it worked. I asume sample mix project start with first embeded mode, but phoenix (which is more complex framework configures it differently)

You can try start iex -S mix again, and you’ll see that the module is not loaded