Compile_env vs get_env and 1.14


We’re in the process of upgrading from 1.13 to 1.14.

We had various calls to compile_env to access env variables, and from what the warnings are telling me these should be get_env.

I’ve replaced these all, going from

@access_key Application.compile_env(:app, key)


defp access_key, do: Application.get_env(:app, :key)

This seems to have worked fine and cleared 99% of the errors, however there is one issue with Guardian that I can’t solve:

defmodule App.Auth.Bla do
  use Guardian,
    otp_app: :app,
    allowed_algos: ["HS512"],
    issuer: "app",
    secret_key: Application.compile_env(:app, __MODULE__)[:secret_key]

Because this call happens outside of a function, I cannot replace it with get_env. But leaving it at compile_env throws an error. I also can’t move it into a function as the function is not yet defined when the above code is invoked.

How can I access an environment variable and feed it into Guardian as the secret key without calling compile_env? Or is there another approach I’m missing?


Can you post the error you get? You should be using compile_env for compile time set config.

With compile_env you want to put the complete path into the function call:
Application.compile_env(:app, [__MODULE__, :secret_key])
That allows elixir to track dependencies better.


Sure, it’s:

== Compilation error in file lib/app/auth/bla.ex ==
** (RuntimeError) Application.compile_env/3 cannot be called inside functions, only in the module body
    (elixir 1.14.0) lib/application.ex:532: Application."MACRO-compile_env"/4
    (elixir 1.14.0) expanding macro: Application.compile_env/2
    lib/app/auth/bla.ex:12: App.Auth.Bla.config/0
    (elixir 1.14.0) expanding macro: Kernel.|>/2
    lib/app/auth/bla.ex:8: App.Auth.Bla.config/

It mentions line 8 and line 12 of the file, which are use Guardian and secret_key: Application.compile_env(:app, __MODULE__)[:secret_key] respectively

Seems like guardian is putting the AST of the call in a function. I’d probably just put assign the secret to a module attribute and give that to guardian, unless you actually need that to be runtime configurable.

Annoyingly I can’t do that

@secret_key Application.compile_env(:app, [__MODULE__, :secret_key])

Throws an error as this variable is different at compile time vs run time, and I can’t call get_env here as it’s outside of a function.

I also can’t do

defp secret_key, do: Application.get_env(:app, [__MODULE__, :secret_key])

as this function isn’t defined when Guardian has its secret key assigned

I might be off here, but if guardian indeed puts the AST for fetching the secret in a function for runtime use and also tries to access it at compile time then I think that’s something to fix on guardian’s end. It should be doing either/or, but not both.

You can keep configuration to the minimum in implementation module of the Guardian - move the rest in config files.

# bla.ex
defmodule App.Auth.Bla do
  use Guardian, otp_app: :app
# sample.config/dev.exs 
config :app, App.Auth.Bla,
  allowed_algos: ["HS512"],
  issuer: "app",
  secret_key: "AppBlaSecretKey"

You can read more in config section of Guardian docs

This will be regular configuration loading from environment, once the implementation module is kept minimal.


This actually works too thank you so much