View module not recompiling after moving in a new HEEX template

Running Elixir 1.15.0 with OTP 25.

3 Times today have gotten an error like:

** (ArgumentError) no “group_without_children” html template defined for SctWeb.FdGroupHTML (the module exists but does define group_without_children/1 nor render/2)

I have tried a number of troubleshooting steps:

  • Check that the embed_templates is correct in the _view file.
  • Check the name of the template is “group_without_children.html.heex”
  • Check that it is in the folder and is in the right folder.
  • rerun tests - still fails

So I backtracked and realized that I actually originally made the file elsewhere and had dragged it into the fd_group_html folder afterwards.

So I tried creating a HEEX template in another folder and added an action that referenced it, and a test for that action. Ran the test and the test of course failed since I have not moved the HEEX template into the correct folder. So now I move the template into the correct folder and rerun the test. Still fails with error like above - the module exists but does not define the template as a 1 arity function. Did all the double checks again (name is correct, folder is correct).

But when I ran “mix test” after moving the template into the proper folder I realized I saw no compiles occur??? Wait… should it not have to recompile the view module with the embed_templates again?

So I made a small change to the view templates module file (added a space before a parameter.

Ran the tests again and this time it compiled 1 .ex - guess what THIS time it discovered the HEEX template.

So my question is what triggers a view module to recompile if you add a new template to the folder referenced in the embed_templates wildcard?

Note: Since I have been doing this for 6 months now and now this is happening I infer it may be related to the Elixir 1.15 compilation optimizations and is not truly a Phoenix issue. Would like to know either way. And if there is some other process I need to go through to announce HEY there is a new template would also like to know.

It should always trigger automatically. Can you consistently reproduce this issue?

I appear to be able to reproduce it. I have NOT tried it with Elixir 1.15.2 nor on OTP 26. But I will reproduce it tomorrow WTP twice to ensure I understand the minimal steps to reproduction.

Perhaps it would be helpful if I knew where in the code this should trigger automatically? I would enjoy looking at that code.

1 Like

I gave it a quick shot just now to repro and it seems pretty easy to reproduce.

Steps:

  • Create a .HEEX in another folder. Named it copy.html.heex
  • Move copy.html.heex into an existing folder containing templates aka “xyz_html” (view reference is “xyz_html/*”
  • change a controller action to reference that in render call. aka :copy
  • run mix test with a test that exercises that controller action
     ** (ArgumentError) no "copy" html template defined for SctWeb.FdGroupHTML  (the module exists but does define copy/1 nor render/2)
     code: get(
     stacktrace:
       (phoenix_template 1.0.2) lib/phoenix/template.ex:248: Phoenix.Template.render_with_fallback/4
       (phoenix_template 1.0.2) lib/phoenix/template.ex:197: Phoenix.Template.render_within_layout/4
       (phoenix 1.7.7) lib/phoenix/controller.ex:1000: anonymous fn/5 in Phoenix.Controller.template_render/4
       (telemetry 1.2.1) /Users/tj/projects/sct_elixir/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
       (phoenix 1.7.7) lib/phoenix/controller.ex:987: Phoenix.Controller.render_with_layouts/4
       (phoenix 1.7.7) lib/phoenix/controller.ex:974: Phoenix.Controller.render_and_send/4
       (sct 0.5.0) lib/sct_web/controllers/sc/perform/foundation_document/fd_group_controller.ex:1: SctWeb.FdGroupController.action/2
       (sct 0.5.0) lib/sct_web/controllers/sc/perform/foundation_document/fd_group_controller.ex:1: SctWeb.FdGroupController.phoenix_controller_pipeline/2
       (phoenix 1.7.7) lib/phoenix/router.ex:430: Phoenix.Router.__call__/5
       (sct 0.5.0) deps/plug/lib/plug/error_handler.ex:80: SctWeb.Router.call/2
       (sct 0.5.0) lib/sct_web/endpoint.ex:1: SctWeb.Endpoint.plug_builder_call/2
       (sct 0.5.0) lib/sct_web/endpoint.ex:1: SctWeb.Endpoint.call/2
       (phoenix 1.7.7) lib/phoenix/test/conn_test.ex:225: Phoenix.ConnTest.dispatch/5
       test/sct_web/controllers/sc/perform/foundation_document/fd_group_controller_test.exs:70: (test)

then I slightly modified the view file (added some spacing)

  • ran mix test again and it compiled 1 .ex and referenced the HEEX this time.
1 Like

I am wondering if this requires that live_reload in config of the endpoint needs to be on for this to recompile. I have that turned off presently, so am going to see if that has anything to do with this detection. It does not seem to be required for running tests of template changes, but it may be for template “additions”.

No - I only had live_reload off in dev and I am running the tests in test env. I was able to reproduce it by merely creating a new template in the folder and then referencing it from an existing action. SO only change was a new template file in the folder and a change to the controller action. I must be missing something.

Is “another folder” still in the same Mix project? Could it be the compiler somehow “seeing” the Heex file at the old location and then not notice when it moves?

Also, what operating system are you on? I’m not sure if it’s related, but I’ve burned too much time chasing “this only happens on Windows with antivirus” bugs to not double-check.

1 Like

Thank you. Can you please try with v1.5.2? If it persists I will take a look at it next.

Yes - same mix project, but I am not doing anything in between to invoke compilation. And I am on MacOS. I am using VS code as IDE, but I also tried same sequence through iterm outside vscode to no avail.

Yes - will try on 1.15.2.

❯ elixir -v
Erlang/OTP 25 [erts-13.2.2.1] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit] [dtrace]

Elixir 1.15.2 (compiled with Erlang/OTP 25)

Was able to reproduce on Elixir 1.15.2. Same steps:

  • Create a new HEEX template in folder already referenced in view module.
  • Change or add a controller action to refer to new template name.
  • Run mix test that smoke tests that action

Result is it recompiles 4 files (in my case) and runs test and fails:

 ** (ArgumentError) no "copy" html template defined for SctWeb.FdGroupHTML  (the module exists but does define copy/1 nor render/2)
  • Make slight change to view module (added a space embed_templates( "fd_group_html/*") )
  • Run mix test

Result is it compiles 1 module (I infer the view module just changed) and it finds the new template.


In itself this is a very low priority issue with an easy workaround. I am here to help try things though in the interest in determining root cause (my fault or otherwise) . Here to help. I am still uncertain if this is a Elixir issue or a Phoenix issue or a Phoenix template issue.

1 Like

I believe I’m facing the same issue, except with NimblePublisher.

Looking at embed_templates macro, it also uses @external_resource/__mix_recompile__? the same as NimblePublisher.

When I upgraded to Elixir 1.15.2 I noticed that my NimblePublisher resources weren’t picking up new files when compiling for prod. Didn’t notice during the dev env because I think Phoenix code_reloader catches the changes. Only when I updated the module that uses NimblePublisher, forcing a recompile in the prod environment, did the rest of the new pages pick up.

I think the key here is the environment change, which doesn’t have Phoenix code_reloader. My issue bubbled up in prod while yours was in test.

I’ve put together a reproduction here:

Running load_in_file.exs and then running mix compile shows that the __mix_recompile__? isn’t firing despite the new file added to content/pages/

A demonstration
~/repos/recompile main
❯ mix compile

~/repos/recompile main
❯ elixir load_in_file.exs
title6520251

~/repos/recompile main*
❯ mix compile

~/repos/recompile main*
❯ iex -S mix phx.server
[info] Running RecompileWeb.Endpoint with cowboy 2.10.0 at 127.0.0.1:4000 (http)
[info] Access RecompileWeb.Endpoint at http://localhost:4000
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

[watch] build finished, watching for changes...
Interactive Elixir (1.15.2) - press Ctrl+C to exit (type h() ENTER for help)

Rebuilding...

Done in 148ms.
iex(1)> Recompile.Content.list_pages()
[
  %Recompile.Content.Page{title: "another", body: "<p>\ntest again</p>\n"},
  %Recompile.Content.Page{title: "the title", body: "<p>\ntesting this</p>\n"}
]
iex(2)>

Thank you. This is fixed and v1.15.3 will be released shortly.

8 Likes

I’ve been getting this all last night and today on 1.14.3, otp 25. Latest Phoenix 1.7.7 though.

Just upgraded to 1.15.2 and same issue. Have just renamed the files in green below to try and trigger anything/something. I have remove _build too:

Keep getting:

 `no "error" html template defined for SentrypeerWeb.EmailVerifyHTML  (the module exists but does not define error/1 nor render/2)`

Here’s that commit now if you’d like to compare to yours:

I found this was fixed in 1.15.3 for me - if it is same problem.

1 Like

I did a copy a file to a new one process too, but was on an older elixir version. Driving me mad :slight_smile:

Exactly the same as my other controllers and html.ex files.

Just a follow up. Tried my code with Elixir 1.15.4, same thing. So must be my code. Will dig in tomorrow.

For goodness sake! My email_verify_html folder was outside my controllers folder. Days!!!

2 Likes