Elixir’s “module is not available” error can drive you nuts.
But fear not:
After writing a lot of Elixir and seeing this error often, I’ll developed a few quick tricks that you can use to solve why your module is not available in seconds instead of minutes.
Here’s 4 quick things you can check.
Check Your Module Definition
This almost goes without saying, but every time I don’t check the module definition first, it turns out I’ve misspelled one of my module definitions.
Check the top (usually top) of your file for your defmodule
function. Some things to look for are that everything is spelled correctly, i.e. App.User
and not App.Users
, that you have included your application name if needed, i.e. App.User
not User
, that you are using the proper punctuation, i.e. App.User.Note
not App.UserNote
.
Did You Forget to include alias
?
Sometimes, in a bout of “in the zone” programming, you may have written a call to a module that you believed you had aliased, but forgot to include the alias
function call.
For example, trying to call User.full_name()
when the full function call is App.User.full_name()
would result in the module is not available error. App.User
, in this case, would need to be aliased first.
Inspect Loaded Modules
Still can’t find the module? Well, one debugging trick is to check a list of all the currently loaded, user-defined modules in the application.
There’s two ways you can do this:
First, you can check the my_app.app
compiled file in your /_build
directory.
Specifically, this file is usually located in:
"_build/#{MIX_ENV}/lib/#{YOUR_APP_NAME_HERE}/ebin"
While this file may appear a bit cryptic, you can probably ascertain what the modules
entry shows. It’s a list of user-defined modules available to your application from compilation.
The second trick is:
Use :application.get_key(:my_app, :modules)
wherever the module is not available error is occurring. Combine this with the IO.inspect/1
function and it will show you all of the available user-defined Elixir modules at that point in time.
IO.inspect :application.get_key(:my_app, :modules)
It’s a quick way to see all the user-defined Elixir modules available to the :my_app
application at that point in your Elixir application.
Using this Elixir debugging trick may allow you to catch if you have badly misspelled a module or perhaps forgot to include a module for compilation.
Which brings us to my last tip:
Make Sure Your Module is Available to Your Application
My last tip is the most complex in terms of implementation and understanding.
But here goes:
You need to check that your Elixir module is available to your application by telling the elixirc
compiler to generate a .beam
file for your module.
Why would the Elixir compiler not compile a .beam
file? Well, 2 common reasons:
- You placed the module in a directory outside of
lib
and did not
tell the compiler. -
You used the
.exs
extension instead of.ex
The first reason can be puzzling if you do not realize the problem. By default, the Elixir compiler only looks to compile files and load modules located in the lib
directory of an Elixir application.
As your Elixir application complexity increases, you may decide to include Elixir modules that need to be compiled outside of the lib
directory. A good example of this are modules that may be used for supporting tests such as mocks.
The solution to this problem is straightforward, but a bit hidden in the Elixir documentation.
You need to tell the Elixir compiler the directory where your modules are located.
To do this, you need to use the Elixir elixirc_paths
option in your project configuration. All you need to do is set the elixirc_paths
key to a list of paths of the directories that you want the Elixir compiler to compile.
Here’s the catch:
In many cases, you probably do not want to compile the same files across different environments.
For example, in a production environment, you may not want to compile the modules that you use for your test environment.
To get around this, we can use a function that we define in mix.exs
that returns a different lists of paths depending on the environment.
In fact, this is exactly what Phoenix Framework does by default in new projects.
def project do
[
...
elixirc_paths: elixirc_paths(Mix.env),
...
]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
You can see in the code above the elixirc_paths/1
function includes the “test/support” directory in the Elixir compiler paths for the test environment, but not for other environments such as development.
Setting this directory in the Elixir compiler paths means that any modules defined within .ex
extension files will automatically be compiled and their modules loaded for your application.
Which brings me to the second issue:
Another mistake that you may make is to use the .exs
extension instead of the .ex
extension. You may be defining Elixir modules that you need to compile for use in your application in files with a .exs
extension.
The .exs
extension is used for scripting. Like the .ex
extension, .exs
files are compiled, their modules loaded, and executed.
It is very well documented that .ex
extension files get compiled down to .beam
files. The modules in your compiled .beam
files get loaded by your application automatically when the application starts.
However, the compiled BEAM bytecode from .exs
files does not get output into a .beam
file the way that .ex
files do. They are, in essence, ephemeral. They are compiled, loaded, and executed, and then disappear. Hence, if you are trying to access a module in an .exs
file from your application, it will fail with the “module not available” error.
Hope this information was helpful. If you have any questions or have another tip, feel free to leave a comment below.
This post is part of my series of Elixir Tutorials
Hey, I’m Adam. I’m guessing you just read this post from somewhere on the interwebs. Hope you enjoyed it.
You can also follow me on the Twitters at: @DeLongShot
I had defmodule BbLearnRestClientTest do but the project is bblearn_rest_client so needed defmodule BblearnRestClientTest do. You just save me hours! Thank you very much.
Ah, yeah, I think I’ve made a similar mistake before too. Glad I could help!
This helped a lot, particularly looking inside the _build dir. erlang was making a .beam that was not reflecting a rename/refactor. The remedy was to delete the file, compile, then add a fresh file with the new name. Fixed it.
Great to hear! I’ve had to check the _build directory before too.