Why is the passed block being evaluated even when an exception is raised?

Yeah to expand on what @sodapopcan if you want to delay the execution of some code without a macro you’d use a function eg:

authorize_role!(from, to, required_role, fn ->
  # stuff that requires the role
end)

If you want to use a do block style then yeah that has to be a macro because you’re essentially re-arranging the AST to wrap the code in the if.