Plug.Conn.__struct__/0 is undefined

:appsignal_plug, a dependency of :appsignal_phoenix, fails on this line, with an error Plug.Conn.__struct__/0 is undefined.

Any idea on what might be causing this, and how to fix it, appreciated. :pray:

What I’ve tried so far:

  • rm -rf _build deps
  • mv ~/.hex/cache.ets ~/.hex/cache.ets.bak
  • locally cloned and successfully compiled :appsignal_plug ( {:appsignal_plug, path: "/appsignal-elixir-plug", override: true})
  • removed :appsignal_* deps, but now :bandit reports same error (module Plug.Conn is not loaded and could not be found).

Full error from mix deps.compile output:

==> appsignal
AppSignal extension installation successful
Compiling 54 files (.ex)
Generated appsignal app
==> appsignal_plug
Compiling 2 files (.ex)
     error: Plug.Conn.__struct__/0 is undefined, cannot expand struct Plug.Conn. Make sure the struct name is correct. If the struct name exists and is correct but it still cannot be found, you likely have cyclic module usage in your code
     │
 100 │   def put_name(%Plug.Conn{} = conn, name) do
     │                ^
     │
     └─ lib/appsignal_plug.ex:100:16: Appsignal.Plug.put_name/2

    error: Plug.Conn.__struct__/0 is undefined, cannot expand struct Plug.Conn. Make sure the struct name is correct. If the struct name exists and is correct but it still cannot be found, you likely have cyclic module usage in your code
    │
  3 │         %Plug.Conn{
    │         ^
    │
    └─ lib/appsignal_plug/metadata.ex:3:9: Appsignal.Metadata.Plug.Conn.metadata/1


== Compilation error in file lib/appsignal_plug.ex ==
** (CompileError) lib/appsignal_plug.ex: cannot compile module Appsignal.Plug (errors have been logged)
    lib/appsignal_plug.ex:100: (module)
could not compile dependency :appsignal_plug, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile appsignal_plug --force", update it with "mix deps.update appsignal_plug" or clean it with "mix deps.clean appsignal_plug"

Odd things noticed:

  • the above error occurs even when referring to a locally-cloned :appsignal_plug ({:appsignal_plug, path: "/appsignal-elixir-plug"}) … even though that locally-cloned repo compiles!
  • :appsignal_plug compiles in another, older instance of my application, on the same server. The problematic application’s mix.lock is identical to the older one’s, regarding the 3 AppSignal libs. Both mix.locks have this:
  "appsignal": {:hex, :appsignal, "2.12.2", "f93bed975c73779e1711fb2196b4c340c50b474ab41a7026e8599b0b4b5db046", [:make, :mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:decorator, "~> 1.2.3 or ~> 1.3", [hex: :decorator, repo: "hexpm", optional: false]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c4063fcd264a5e372299bec15bbf35a057ae5912c739d374616e0a33c0cc9ef0"},
  "appsignal_phoenix": {:hex, :appsignal_phoenix, "2.4.0", "150cf8753e3f0a6352504886808c822a4b8099afdfb4b8ff98cbd8d49c99663d", [:mix], [{:appsignal, ">= 2.11.0 and < 3.0.0", [hex: :appsignal, repo: "hexpm", optional: false]}, {:appsignal_plug, ">= 2.0.15 and < 3.0.0", [hex: :appsignal_plug, repo: "hexpm", optional: false]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.9 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2808cbf12c0ab16ae909ccbae1af758d7478e6165a4324277f701bafbcf70aa1"},
  "appsignal_plug": {:hex, :appsignal_plug, "2.0.15", "758a8a78944878e8461bbc77ca86219121a56f4299c6d79940ab083cf9afea00", [:mix], [{:appsignal, ">= 2.7.6 and < 3.0.0", [hex: :appsignal, repo: "hexpm", optional: false]}, {:plug, ">= 1.1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1c6059049e2081e808aaef04e2b9917e06277f61a35a0e103db860d08cbc41f1"}
  • I checked the 2 myapp versions for any difference in Plug usage, but grep Plug /myapp{newer,older}/lib/**/*.ex output is identical.

Env

Elixir 1.17.1
Erlang 27.0
Ubuntu 20.04.6

Can you try:

mix deps.compile --force

This should force recompilation of all the dependencies.

If this doesn’t work, can you try removing the _build folder completely and trying again?

Thanks. I already tried both. (Even though the rm cmd in OP had incorrect/reversed dir names – it should be rm -rf _build deps – I did do rm against correct dirs.)

I now realised that mix.exs still had a :plug reference to a :path from a local computer, but which doesn’t exist on the server:

{:plug, path: "/local-dev/plug", override: true, only: :dev}

Removing the above fixed the issue. :tada:

But can you advise how to avoid such issue in the future? Why did…

  1. mix deps.get --only prod
  2. mix deps.compile

…keep “silently” failing? “silently” as in not pointing to the actual cause of the issue (if this was indeed the “root” cause). mix deps.get without the --only prod flag does warn that the above :plug :path doesn’t exist.

In mix.lock all :plug references seemed to be referring to "hexpm", even with the above {:plug, ..., only: :dev}. F.e. ref for "phoenix" in mix.lock:

{:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]

If it matters, MIX_ENV was set to prod throughout.

What could I’ve done better?

3 Likes