FabianDaniel00
Hi everyone!
I trying to create a Phoenix LiveView dependency to authorize the lifecycle stages with attach_hook/4 via on_mount/4 callback.
I need to attach hook to :mount stage and it possible with the on_mount/4 callback as described here. I don’t know why I getting the following error:
# ArgumentError at GET /some/route
Exception:
** (ArgumentError) invalid lifecycle event provided to attach_hook.
Expected one of: :handle_event | :handle_info | :handle_params
Got: :mount
(phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:61: Phoenix.LiveView.Lifecycle.attach_hook/4
(elixir 1.15.7) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
(dronteh 0.1.0) lib/my_dependency_web/on_mount.ex:6: MyDependency.OnMount.on_mount/4
(phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:149: anonymous fn/4 in Phoenix.LiveView.Lifecycle.mount/3
(phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:212: Phoenix.LiveView.Lifecycle.reduce_socket/3
(phoenix_live_view 0.20.1) lib/phoenix_live_view/utils.ex:392: anonymous fn/6 in Phoenix.LiveView.Utils.maybe_call_live_view_mount!/5
(telemetry 1.2.1) /app/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
(phoenix_live_view 0.20.1) lib/phoenix_live_view/static.ex:277: Phoenix.LiveView.Static.call_mount_and_handle_params!/5
(phoenix_live_view 0.20.1) lib/phoenix_live_view/static.ex:118: Phoenix.LiveView.Static.render/3
(phoenix_live_view 0.20.1) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
(phoenix 1.7.9) lib/phoenix/router.ex:432: Phoenix.Router.__call__/5
(dronteh 0.1.0) lib/dronteh_web/endpoint.ex:1: DrontehWeb.Endpoint.plug_builder_call/2
(dronteh 0.1.0) deps/plug/lib/plug/debugger.ex:136: DrontehWeb.Endpoint."call (overridable 3)"/2
(dronteh 0.1.0) lib/dronteh_web/endpoint.ex:1: DrontehWeb.Endpoint.call/2
(phoenix 1.7.9) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
(plug_cowboy 2.6.1) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
(cowboy 2.10.0) /app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy 2.10.0) /app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
(cowboy 2.10.0) /app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
(stdlib 5.1.1) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Code:
`lib/phoenix_live_view/lifecycle.ex`
56 hooks ++ [hook]
57 end)
58 end
59
60 def attach_hook(%Socket{}, _id, stage, _fun) do
61> raise ArgumentError, """
62 invalid lifecycle event provided to attach_hook.
63
64 Expected one of: :handle_event | :handle_info | :handle_params
65
66 Got: #{inspect(stage)}
`lib/enum.ex`
2505 operation cannot be expressed by any of the functions in the `Enum`
2506 module, developers will most likely resort to `reduce/3`.
2507 """
2508 @spec reduce(t, acc, (element, acc -> acc)) :: acc
2509 def reduce(enumerable, acc, fun) when is_list(enumerable) do
2510> :lists.foldl(fun, acc, enumerable)
2511 end
2512
2513 def reduce(first..last//step, acc, fun) do
2514 reduce_range(first, last, step, acc, fun)
2515 end
`lib/my_dependency_web/on_mount.ex`
1 defmodule MyDependency do
2 import Phoenix.LiveView
3
4 def on_mount(:default, _params, _session, socket) do
5 # some code...
6 socket = attach_hook(socket, "protect_mount", :mount, fn _params, _session, socket) ->
7 # some code...
8
9 {:cont, socket} or {:halt socket} # with redirect
10 end)
11
`lib/phoenix_live_view/lifecycle.ex`
144 end
145
146 @doc false
147 def mount(params, session, %Socket{private: %{@lifecycle => lifecycle}} = socket) do
148 reduce_socket(lifecycle.mount, socket, fn %{id: {_mod, arg}} = hook, acc ->
149> case hook.function.(arg, params, session, acc) do
150 {:halt, %Socket{redirected: nil}} ->
151 raise_halt_without_redirect!(hook)
152
153 {:cont, %Socket{redirected: to}} when not is_nil(to) ->
154 raise_continue_with_redirect!(hook)
`lib/phoenix_live_view/lifecycle.ex`
207
208 new_socket
209 end
210
211 defp reduce_socket([hook | hooks], acc, function) do
212> case function.(hook, acc) do
213 {:cont, %Socket{} = socket} -> reduce_socket(hooks, socket, function)
214 {:halt, %Socket{} = socket} -> {:halt, socket}
215 other -> bad_lifecycle_response!(other, hook)
216 end
217 end
`lib/phoenix_live_view/utils.ex`
387 :telemetry.span(
388 [:phoenix, :live_view, :mount],
389 %{socket: socket, params: params, session: session, uri: uri},
390 fn ->
391 socket =
392> case Lifecycle.mount(params, session, socket) do
393 {:cont, %Socket{} = socket} when exported? ->
394 view.mount(params, session, socket)
395
396 {_, %Socket{} = socket} ->
397 {:ok, socket}
`/app/deps/telemetry/src/telemetry.erl`
316 EventPrefix ++ [start],
317 #{monotonic_time => StartTime, system_time => erlang:system_time()},
318 merge_ctx(StartMetadata, DefaultCtx)
319 ),
320
321> try {_, #{}} = SpanFunction() of
322 {Result, StopMetadata} ->
323 StopTime = erlang:monotonic_time(),
324 execute(
325 EventPrefix ++ [stop],
326 #{duration => StopTime - StartTime, monotonic_time => StopTime},
`lib/phoenix_live_view/static.ex`
272
273 defp call_mount_and_handle_params!(socket, view, session, params, uri) do
274 mount_params = if socket.router, do: params, else: :not_mounted_at_router
275
276 socket
277> |> Utils.maybe_call_live_view_mount!(view, mount_params, session, uri)
278 |> mount_handle_params(view, params, uri)
279 |> case do
280 {:noreply, %Socket{redirected: {:live, _, _}} = socket} ->
281 {:stop, socket}
282
`lib/phoenix_live_view/static.ex`
113 action,
114 flash,
115 host_uri
116 )
117
118> case call_mount_and_handle_params!(socket, view, mount_session, conn.params, request_url) do
119 {:ok, socket} ->
120 data_attrs = [
121 phx_session: sign_root_session(socket, router, view, to_sign_session, live_session),
122 phx_static: sign_static_token(socket)
123 ]
`lib/phoenix_live_view/controller.ex`
34 end
35 end
36
37 """
38 def live_render(%Plug.Conn{} = conn, view, opts \\ []) do
39> case LiveView.Static.render(conn, view, opts) do
40 {:ok, content, socket_assigns} ->
41 conn
42 |> Phoenix.Controller.put_view(LiveView.Static)
43 |> Phoenix.Controller.render(
44 "template.html",
`lib/phoenix/router.ex`
427 :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
428 halted_conn
429
430 %Plug.Conn{} = piped_conn ->
431 try do
432> plug.call(piped_conn, plug.init(opts))
433 else
434 conn ->
435 measurements = %{duration: System.monotonic_time() - start}
436 metadata = %{metadata | conn: conn}
437 :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
`lib/dronteh_web/endpoint.ex`
1> defmodule DrontehWeb.Endpoint do
2 use Phoenix.Endpoint, otp_app: :dronteh
3
4 # The session will be stored in the cookie and signed,
5 # this means its contents can be read but not tampered with.
6 # Set :encryption_salt if you would also like to encrypt it.
`deps/plug/lib/plug/debugger.ex`
131 case conn do
132 %Plug.Conn{path_info: ["__plug__", "debugger", "action"], method: "POST"} ->
133 Plug.Debugger.run_action(conn)
134
135 %Plug.Conn{} ->
136> super(conn, opts)
137 end
138 rescue
139 e in Plug.Conn.WrapperError ->
140 %{conn: conn, kind: kind, reason: reason, stack: stack} = e
141 Plug.Debugger.__catch__(conn, kind, reason, stack, @plug_debugger)
`lib/dronteh_web/endpoint.ex`
1> defmodule DrontehWeb.Endpoint do
2 use Phoenix.Endpoint, otp_app: :dronteh
3
4 # The session will be stored in the cookie and signed,
5 # this means its contents can be read but not tampered with.
6 # Set :encryption_salt if you would also like to encrypt it.
`lib/phoenix/endpoint/sync_code_reload_plug.ex`
17
18 def call(conn, {endpoint, opts}), do: do_call(conn, endpoint, opts, true)
19
20 defp do_call(conn, endpoint, opts, retry?) do
21 try do
22> endpoint.call(conn, opts)
23 rescue
24 exception in [UndefinedFunctionError] ->
25 case exception do
26 %UndefinedFunctionError{module: ^endpoint} when retry? ->
27 # Sync with the code reloader and retry once
`lib/plug/cowboy/handler.ex`
6 def init(req, {plug, opts}) do
7 conn = @connection.conn(req)
8
9 try do
10 conn
11> |> plug.call(opts)
12 |> maybe_send(plug)
13 |> case do
14 %Plug.Conn{adapter: {@connection, %{upgrade: {:websocket, websocket_args}} = req}} = conn ->
15 {handler, state, cowboy_opts} = websocket_args
16 {__MODULE__, copy_resp_headers(conn, req), {handler, state}, cowboy_opts}
`/app/deps/cowboy/src/cowboy_handler.erl`
32 -optional_callbacks([terminate/3]).
33
34 -spec execute(Req, Env) -> {ok, Req, Env}
35 when Req::cowboy_req:req(), Env::cowboy_middleware:env().
36 execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
37> try Handler:init(Req, HandlerOpts) of
38 {ok, Req2, State} ->
39 Result = terminate(normal, Req2, State, Handler),
40 {ok, Req2, Env#{result => Result}};
41 {Mod, Req2, State} ->
42 Mod:upgrade(Req2, Env, Handler, State);
`/app/deps/cowboy/src/cowboy_stream_h.erl`
301 end.
302
303 execute(_, _, []) ->
304 ok;
305 execute(Req, Env, [Middleware|Tail]) ->
306> case Middleware:execute(Req, Env) of
307 {ok, Req2, Env2} ->
308 execute(Req2, Env2, Tail);
309 {suspend, Module, Function, Args} ->
310 proc_lib:hibernate(?MODULE, resume, [Env, Tail, Module, Function, Args]);
311 {stop, _Req2} ->
`/app/deps/cowboy/src/cowboy_stream_h.erl`
290 %% to simplify the debugging of errors. The proc_lib library
291 %% already adds the stacktrace to other types of exceptions.
292 -spec request_process(cowboy_req:req(), cowboy_middleware:env(), [module()]) -> ok.
293 request_process(Req, Env, Middlewares) ->
294 try
295> execute(Req, Env, Middlewares)
296 catch
297 exit:Reason={shutdown, _}:Stacktrace ->
298 erlang:raise(exit, Reason, Stacktrace);
299 exit:Reason:Stacktrace when Reason =/= normal, Reason =/= shutdown ->
300 erlang:raise(exit, {Reason, Stacktrace}, Stacktrace)
`proc_lib.erl`
No code available.
## Connection details
### Params
%{}
### Request info
* URI: http://localhost:4000/some/route
* Query string:
### Headers
* accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
* accept-encoding: gzip, deflate, br
* accept-language: en-US,en;q=0.9,hu;q=0.8,sr;q=0.7
* cache-control: max-age=0
* connection: keep-alive
* cookie: _dronteh_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYNEZzX2RLRWpkOXBqbFE1TGJvR0lqcVI2bQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6M2E4VW9vN1hBRm51S3hZTWF2S2cyN0RmNHFVZlltQk1obDBZczBxTXBSRT1tAAAACnVzZXJfdG9rZW5tAAAAIN2vFKKO1wBZ7isWDGryoNuw3-KlH2JgTIZdGLNKjKUR.lBUv7RmSNBZqPQ9UZHJ2Lb8_KokcFoukItDpXDw-aGw
* host: localhost:4000
* referer: http://localhost:4000/some/route
* sec-ch-ua: "Brave";v="119", "Chromium";v="119", "Not?A_Brand";v="24"
* sec-ch-ua-mobile: ?0
* sec-ch-ua-platform: "Windows"
* sec-fetch-dest: document
* sec-fetch-mode: navigate
* sec-fetch-site: same-origin
* sec-gpc: 1
* upgrade-insecure-requests: 1
* user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
### Session
%{"_csrf_token" => "4Fs_dKEjd9pjlQ5LboGIjqR6", "live_socket_id" => "users_sessions:3a8Uoo7XAFnuKxYMavKg27Df4qUfYmBMhl0Ys0qMpRE=", "user_token" => <<221, 175, 20, 162, 142, 215, 0, 89, 238, 43, 22, 12, 106, 242, 160, 219, 176, 223, 226, 165, 31, 98, 96, 76, 134, 93, 24, 179, 74, 140, 165, 17>>}
My code is the following:
defmodule MyAppWeb.SomeLive do
use MyAppWeb, :live_view
on_mount MyDependency
def mount(_params, _session, socket) do
# some code...
{:ok, socket}
end
# other code...
end
defmodule MyDependency do
import Phoenix.LiveView
def on_mount(:default, _params, _session, socket) do
# some code...
socket = attach_hook(socket, "protect_mount", :mount, fn _params, _session, socket) ->
# some code...
{:cont, socket} or {:halt socket} # with redirect
end)
{:cont, socket}
end
end
Of course my code has more logic but this is the main idea. I got that error in other projects too.
Can somebody tell me what I doing wrong? ![]()
Versions I use:
- elixir: ~> 1.15.7
- phoenix: ~> 1.7.9
- phoenix_live_view: ~> 0.20.1
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 2- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
codeanpeace
Welcome!
No need for this
attach_hooksinceon_mountis already a hook on mount which is whyattach_hookdoesn’t accept:mountas a valid lifecycle event.Instead, give something like this a try:
FabianDaniel00
Thanks for the reply!

Hmm yeah this is makes sense. It seems like I misunderstood the docs.
My goal is the user can write pattern matching scenarios to those methods which can be attached hook to and pattern match such as the
:mountstage as I already did with:handle_paramsand the other lifecycle methods via theattach_hook/4. But I think I can do that what you suggested but with some macro.