moxley
Uncaught throw when error is returned from nested action call
I have a nested set of action calls, like this:
Action A → Action B
When the change function of Action B adds an error to its changeset, it somehow results in an uncaught throw in the call to Action B. This means that Action A can’t act on the return value from Action B when Action B returns an error.
Here’s approximately what the Action A change function might look like:
defmodule ResourceA.ActionA do
def change(changeset, _context) do
Ash.Changeset.before_action(changeset, fn changeset ->
result = ResourceB.action_b()
# If action_b() adds an error to its changeset,
# the result can't be acted upon here, because there is a throw generated internally.
changeset
end)
end
end
Here is an example repo that demonstrates the issue: hairbnb2/test/hairbnb/cart_test.exs at main · moxley/hairbnb2 · GitHub
I’ve tried using before_action, after_action, and manual, and all three techniques have the same issue.
Marked As Solved
zachdaniel
Yeah, so any errors that happen inside of action hooks will incur a rollback (the throwing behavior you’re describing), and the primary reason for this is that this is actually a situation that can be forced by some of the errors that can come back from a data layer. So for example, if you violate a unique constraint in the db, the transaction has to be aborted (and so you’d want an after_transaction hook in place on the resource that started the transaction to handle/correct that.
If you are aware that an action you’re calling may fail in a hook, you can use the rollback_on_error?: false option when calling the action.
With all of that said, an argument could be made that that is not the best design/doesn’t follow the principle of least surprise. We could introduce a backwards compatibility config that will modify the default of that option to false instead relatively easily, which would make it such that only specific errors going wrong would rollback the transaction.
Also Liked
zachdaniel
I get what you’re saying, the fundamental issue here is that Ash doesn’t know what the wrapping transaction is going to try to do after the error occurs. The fundamental issue here is around how after_transaction hooks work. If you didn’t start the transaction, then your after_transactions won’t be the one to compensate for errors that your action returns from within the scope of what would be its own transaction. Remember that there is no such thing as a nested transaction (there are savepoints but that’s a separate conversation).
But this is the point of the rollback_on_error? option. You should be able to specify rollback_on_error?: false and then handle whatever error occurs from your action, assuming the error was not an error directly from the data layer (in which case we always roll back).
Last Post!
zachdaniel
I get what you’re saying, the fundamental issue here is that Ash doesn’t know what the wrapping transaction is going to try to do after the error occurs. The fundamental issue here is around how after_transaction hooks work. If you didn’t start the transaction, then your after_transactions won’t be the one to compensate for errors that your action returns from within the scope of what would be its own transaction. Remember that there is no such thing as a nested transaction (there are savepoints but that’s a separate conversation).
But this is the point of the rollback_on_error? option. You should be able to specify rollback_on_error?: false and then handle whatever error occurs from your action, assuming the error was not an error directly from the data layer (in which case we always roll back).
Popular in Questions
Other popular topics
Latest Ash Threads
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









