AF
Ash Frameworkβ€’2mo ago
Vic

Ash policies breakdown not showing help_text

I have this policy (check if the role theyre trying to change the user into is super_admin and if theyre not a super_admin themselves block them)
policy action(:change_role) do
forbid_if expr(
args(:role) == :super_admin &&
^actor(:role) != :super_admin
)
policy action(:change_role) do
forbid_if expr(
args(:role) == :super_admin &&
^actor(:role) != :super_admin
)
it fails with this error
dmin"}}
[error] Jnb.Accounts.User.change_role


Policy Breakdown
user: %{id: "dc6dab27-b724-4f6b-b118-78e47c0ad1fc"}

Policy | πŸ”Ž:

condition: action == :change_role

forbid if: args(:role) == :super_admin && :super_admin != :super_admin | ? | πŸ”Ž

SAT Solver statement:

"action == :change_role" and
(("action == :change_role" and false) or not "action == :change_role")
dmin"}}
[error] Jnb.Accounts.User.change_role


Policy Breakdown
user: %{id: "dc6dab27-b724-4f6b-b118-78e47c0ad1fc"}

Policy | πŸ”Ž:

condition: action == :change_role

forbid if: args(:role) == :super_admin && :super_admin != :super_admin | ? | πŸ”Ž

SAT Solver statement:

"action == :change_role" and
(("action == :change_role" and false) or not "action == :change_role")
and this is my dev.exs relevant config
config :ash,
policies: [
log_policy_breakdowns: :error,
log_successful_policy_breakdowns: :info,
help_text?: true
]
config :ash,
policies: [
log_policy_breakdowns: :error,
log_successful_policy_breakdowns: :info,
help_text?: true
]
Why am I not seing the helper text? Also why can't I access the roles args? or why is my policy not evaluated? This is on a normal liveview, the user IS super_admin, and I have commented out all other policies, (except the AshAuthentication) so I am not sure why its returning forbidden, the user I am trying to modify as a test is myself, also, according to docs I should use ^arg() but whenever I try that I get that its not defined, am I missing an import? aside from the config like this in the ash.Resource
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer],
extensions: [AshAuthentication]
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer],
extensions: [AshAuthentication]
Solution:
fixed
Jump to solution
21 Replies
ZachDaniel
ZachDanielβ€’2mo ago
Hmm... not sure about the help_text not working, you may need to show an issue But for args, you need ^arg(:role)
Vic
VicOPβ€’2mo ago
I'll clean deps and try later to see if I can repeat the logs issues, but right now the ^arg() I get this error
[error] GenServer #PID<0.1225.0> terminating
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in bulk update: Jnb.Accounts.User.change_role
> Exception raised in: Jnb.Accounts.User.change_role

Unknown Error

* ** (BadMapError) expected a map, got: [tenant: nil, args: %{role: :super_admin}, context: %{private: %{actor: %Jnb.Accounts.User{id: "uuid in here
", email: #Ash.CiString<"some@mail.com">, role: :super_admin, confirmed_at: ~U[2025-08-31 21:38:09.704154Z], __meta__: #Ecto.Schema.Metadata<:loaded, "users">}, authorizer_log?: false, authorize?: true, pre_flight_authorization?: false}, data_layer: %{use_atomic_update_data?: true}, changed?: true}, changeset: #Ash.Changeset<domain: Jnb.Accounts, action_type: :update, action: :change_role, attributes: %{role: :super_admin}, relationships: %{}, arguments: %{role: :super_admin}, errors: [], data: %Jnb.Accounts.User{id: "dc6dab27-b724-4f6b-b118-78e47c0ad1fc", email: #Ash.CiString<"some@mail.com">, role: :super_admin, confirmed_at: ~U[2025-08-31 21:38:09.704154Z], __meta__: #Ecto.Schema.Metadata<:loaded, "users">}, context: %{data_layer: %{use_atomic_update_data?: true}, changed?: true}, valid?: true>]
[error] GenServer #PID<0.1225.0> terminating
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in bulk update: Jnb.Accounts.User.change_role
> Exception raised in: Jnb.Accounts.User.change_role

Unknown Error

* ** (BadMapError) expected a map, got: [tenant: nil, args: %{role: :super_admin}, context: %{private: %{actor: %Jnb.Accounts.User{id: "uuid in here
", email: #Ash.CiString<"some@mail.com">, role: :super_admin, confirmed_at: ~U[2025-08-31 21:38:09.704154Z], __meta__: #Ecto.Schema.Metadata<:loaded, "users">}, authorizer_log?: false, authorize?: true, pre_flight_authorization?: false}, data_layer: %{use_atomic_update_data?: true}, changed?: true}, changeset: #Ash.Changeset<domain: Jnb.Accounts, action_type: :update, action: :change_role, attributes: %{role: :super_admin}, relationships: %{}, arguments: %{role: :super_admin}, errors: [], data: %Jnb.Accounts.User{id: "dc6dab27-b724-4f6b-b118-78e47c0ad1fc", email: #Ash.CiString<"some@mail.com">, role: :super_admin, confirmed_at: ~U[2025-08-31 21:38:09.704154Z], __meta__: #Ecto.Schema.Metadata<:loaded, "users">}, context: %{data_layer: %{use_atomic_update_data?: true}, changed?: true}, valid?: true>]
in plural args, I also get this
error: undefined function args/1 (there is no such import)
β”‚
284 β”‚ ^args(:role) == :super_admin &&
β”‚ ^
β”‚
└─ lib/jnb/accounts/user.ex:284:20: Jnb.Accounts.User (module)
error: undefined function args/1 (there is no such import)
β”‚
284 β”‚ ^args(:role) == :super_admin &&
β”‚ ^
β”‚
└─ lib/jnb/accounts/user.ex:284:20: Jnb.Accounts.User (module)
and the log message does show the atom in place of the role
args: %{role: :super_admin}
args: %{role: :super_admin}
so its not that role is not an atom
ZachDaniel
ZachDanielβ€’2mo ago
^arg(:foo) Hmmm like singular, not args
Vic
VicOPβ€’2mo ago
Yeah yeah, arg singular gives me the first long message And args plural doesn’t compile
ZachDaniel
ZachDanielβ€’2mo ago
Hmm...are you fully up to date on your deps?
Vic
VicOPβ€’2mo ago
this is the action in case it helps as well
update :change_role do
argument :role, Jnb.Accounts.User.Roles do
allow_nil? false
end

change set_attribute(:role, arg(:role))
end
update :change_role do
argument :role, Jnb.Accounts.User.Roles do
allow_nil? false
end

change set_attribute(:role, arg(:role))
end
I believe so I'll run a clean and update just in case
ZachDaniel
ZachDanielβ€’2mo ago
whats the stack trace for that issue?
Vic
VicOPβ€’2mo ago
heres the full log
Vic
VicOPβ€’2mo ago
No update was needed, everything was up to date
Vic
VicOPβ€’2mo ago
Decided to simplify it even further, to simply dont allow super_admin to be assigned, to simply see if it was in the action directly or in the policy check but I got the same error

policy action(:change_role) do
forbid_if expr(
^arg(:role) == :super_admin
)
end

policy action(:change_role) do
forbid_if expr(
^arg(:role) == :super_admin
)
end
ZachDaniel
ZachDanielβ€’2mo ago
Can I see the code where you are calling this? Something is clearly going wrong
Vic
VicOPβ€’2mo ago
Sure sure, its a liveview form, let me send the relevant snippets this is the form

<.form for={@account_form} phx-submit="update_role">
<.input
field={@account_form[:role]}
options={Jnb.Accounts.User.Roles.options()}
type="select"
label="Role"
/>
<.button>Update User</.button>
</.form>

<.form for={@account_form} phx-submit="update_role">
<.input
field={@account_form[:role]}
options={Jnb.Accounts.User.Roles.options()}
type="select"
label="Role"
/>
<.button>Update User</.button>
</.form>
the handle event
def handle_event("update_role", %{"form" => params}, socket) do
socket.assigns.account_form |> AshPhoenix.Form.submit(params: params)

{:noreply, socket}
end
def handle_event("update_role", %{"form" => params}, socket) do
socket.assigns.account_form |> AshPhoenix.Form.submit(params: params)

{:noreply, socket}
end
and the form assignment
account_form =
socket.assigns.current_user
|> AshPhoenix.Form.for_update(:change_role, actor: socket.assigns.current_user)

assign(socket, form: to_form(form)) |> assign(account_form: to_form(account_form))
account_form =
socket.assigns.current_user
|> AshPhoenix.Form.for_update(:change_role, actor: socket.assigns.current_user)

assign(socket, form: to_form(form)) |> assign(account_form: to_form(account_form))
theres another form in the page but the @account_form is the one I am using, the other forms params come as "teams" and this one come as "form"
ZachDaniel
ZachDanielβ€’2mo ago
Seems pretty straight forward... is this a private code base? Or something you could share? I might need a reproduction Its probably an easy fix, somewhere we're passing opts when we should be passing the args etc.
Vic
VicOPβ€’2mo ago
sadly it is a private repo, I'll see if I can create a small reproduction later tonight or tomorrow
ZachDaniel
ZachDanielβ€’2mo ago
πŸ™‡β€β™‚οΈ
Vic
VicOPβ€’2mo ago
Finally got a chance to make it, here it is https://github.com/Alt-iOS/simple_demo
GitHub
GitHub - Alt-iOS/simple_demo: bug in policies in ash
bug in policies in ash. Contribute to Alt-iOS/simple_demo development by creating an account on GitHub.
Vic
VicOPβ€’2mo ago
Its the bare minimum to get the error I was getting before and also should fail to print the help text So two birds with one stone
ZachDaniel
ZachDanielβ€’2mo ago
nice. Please open an issue on ash w/ that repro and any other relevant info πŸ™‡β€β™‚οΈ
Vic
VicOPβ€’2mo ago
Of course, and thank you for all the help!!
Solution
ZachDaniel
ZachDanielβ€’2mo ago
fixed
Vic
VicOPβ€’2mo ago
Thanks a lot man!

Did you find this page helpful?