spacebat
Generic actions vs Splode errors
I’ve been using generic actions lately but when they need to return {:error, whatever} things go a bit haywire. Remembering something about Splode from the changelogs I attempted to create custom errors for my application, following the get started with splode guide.
That produces errors that seem to do the right thing, but when a generic action returns {:error, %MyApp.Errors.CustomError{...}} I get a different error.
The following reproduces it:
Mix.install(
[
{:ash, "~> 3.0"}
],
consolidate_protocols: false
)
defmodule Accounts.Errors do
use Splode, error_classes: [
invalid: Accounts.Errors.Invalid,
unknown: Accounts.Errors.Unknown,
custom: Accounts.Errors.Custom
],
unknown_error: Accounts.Errors.Unknown.Unknown
end
defmodule Accounts.Errors.Custom do
use Splode.ErrorClass, class: :custom
end
defmodule Accounts.Errors.CustomError do
use Splode.Error, fields: [:thing, :message], class: :custom
def message(%{thing: thing, message: message}) do
"Custom error for #{thing}: #{message}"
end
end
defmodule Accounts.Profile do
use Ash.Resource,
domain: Accounts,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read, :destroy, :create, :update]
action :fun, :term do
run fn _, _ ->
{:error, Accounts.Errors.CustomError.exception(thing: "city hall", message: "really has gone to pot")}
end
end
end
end
defmodule Accounts do
use Ash.Domain, validate_config_inclusion?: false
resources do
resource Accounts.Profile do
define :fun
end
end
end
err = Accounts.Errors.CustomError.exception(thing: "city hall", message: "gone to pot")
IO.inspect(err, label: "error at toplevel")
IO.puts("error from generic action")
Accounts.fun()
# $ elixir ash_error_issue.ex
# error at toplevel: %Accounts.Errors.CustomError{
# thing: "city hall",
# message: "gone to pot",
# splode: nil,
# bread_crumbs: [],
# vars: [],
# path: [],
# stacktrace: #Splode.Stacktrace<>,
# class: :custom
# }
# error from generic action
# ** (UndefinedFunctionError) function nil.exception/1 is undefined
# nil.exception([errors: [%Accounts.Errors.CustomError{thing: "city hall", message: "really has gone to pot", splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #Splode.Stacktrace<>, class: :custom}], splode: Ash.Error])
# (ash 3.0.9) /home/.../elixir-1.16.2-erts-14.0.2/11d33faefc03b6705b27e772a36495ef/deps/splode/lib/splode.ex:211: Ash.Error.to_class/2
# (ash 3.0.9) lib/ash/error/error.ex:66: Ash.Error.to_error_class/2
# (ash 3.0.9) lib/ash.ex:1306: Ash.run_action/2
# ash_error_issue.ex:64: (file)
Most Liked
zachdaniel
Sorry, I’m not saying there isn’t a bug here. If you return {:error, :enoent} we should accept any value, and unknown values should ultimately be wrapped in an unknown error. Please open a bug in ash for that not working.
In the meantime:
You don’t need to set up your own entire Splode system to have custom errors. If you want to create your own “proper” invalid error, you’d only need to define the individual error:
defmodule MyApp.Errors.FileDoesntExist do
use Splode.Error, fields: [:file], class: :invalid
def message(%{file: file}) do
"File #{file} doesn't exist."
end
end
and then return {:error, MyApp.Errors.FileDoesntExist.exception(file: "filename")}
The bit about error classes is that there are only four error classes that Ash knows about.
:forbidden, :invalid, :framework and :unknown. Errors with a different class should be treated as :unknown, but you’re clearly having a bug where thats not happening.
zachdaniel
Yeah, that looks like a mistake on our part. We are saying UnknownError.exception(value: ...) somewhere where we should be saying UnknownError.exception(message: ...). Could you open an issue w/ that on ash?








