sheerlox

sheerlox

Currently migrating from 2.x to 3.x!

My test suite detected a behavior change that I couldn’t explain by looking at the upgrade doc or the new documentation, so I’m really not sure how I should handle this.

I got an update action that starts the email change process, which is basically just my Ash version of what is found in the mix phx.gen.auth generator.

If the email is already taken, I add an error with Changeset.add_error/2 in the first before_transaction hook:

update :request_email_change do
  description """
  Create a user token for changing the credential's email and send a confirmation email.
  """

  accept [:email]

  argument :current_password, :string, allow_nil?: false, sensitive?: true

  argument :update_email_url_fun, :function,
    allow_nil?: false,
    description: """
    A function taking the confirmation token as its only argument and returning the URL to send to the user.
    """

  validate changing(:email)

  change fn changeset, %{actor: actor} ->
    changeset
    |> Changeset.before_transaction(fn changeset ->
      case changeset
           |> Changeset.get_attribute(:email)
           |> UserCredential.get_by_email(actor: actor) do
        {:ok, _} ->
          Changeset.add_error(
            changeset,
            Ash.Error.Changes.InvalidArgument.exception(
              field: :email,
              message: "has already been taken"
            )
          )

        {:error, _} ->
          changeset
      end
    end)
    |> Changeset.before_transaction(&validate_current_password/1)
    |> Changeset.before_action(fn changeset ->
      current_email = Changeset.get_data(changeset, :email)
      update_email_url_fun = Changeset.get_argument(changeset, :update_email_url_fun)

      {:ok, applied_credential} = Ash.Changeset.apply_attributes(changeset)

      Accounts.deliver_credential_update_email_instructions(
        Ash.load!(applied_credential, [:user], actor: actor),
        current_email,
        update_email_url_fun
      )

      # clear the change so no update happens yet
      Changeset.clear_change(changeset, :email)
    end)
  end
end

I’m almost 100% positive that previously this prevented the execution of the next hooks.

The following test makes sure the email validation uniqueness works, and was passing before upgrading to 3.0:

test "validates email uniqueness", %{user: user, credential: credential} do
  %{email: email} = user_registration_fixture().credential

  assert_raise Ash.Error.Invalid,
               ~r/email: has already been taken/,
               fn ->
                 UserCredential.request_email_change!(
                   credential,
                   email,
                   valid_user_credential_password(),
                   &update_email_url_fun/1,
                   actor: user
                 )
               end
end

Instead of raising the expected Ash.Error.Invalid as before, I know get a Ash.Error.Unknown because there is a match error in the before_action hook (when matching {:ok, applied_credential}), despite my InvalidArgument error being present in the changeset:

%Ash.Error.Unknown{
  changeset: "#Changeset<>",
  errors: [
    %Ash.Error.Unknown.UnknownError{
      error: %MatchError{
        term: {:error,
         #Ash.Changeset<
           domain: TalentIdeal.Accounts,
           action_type: :update,
           action: :request_email_change,
           attributes: %{
             email: #Ash.CiString<"user-576460752303423358@example.com">
           },
           relationships: %{},
           arguments: %{ ... },
           errors: [
             %Ash.Error.Changes.InvalidArgument{
               field: :email,
               message: "has already been taken",
               value: nil,
               splode: nil,
               bread_crumbs: [],
               vars: [],
               path: [],
               stacktrace: #Splode.Stacktrace<>,
               class: :invalid
             }
           ],
           data: #TalentIdeal.Accounts.UserCredential< ... >,
           valid?: false
         >}
      },
      field: nil,
      splode: Ash.Error,
      bread_crumbs: [],
      vars: [],
      path: [],
      stacktrace: #Splode.Stacktrace<>,
      class: :unknown
    }
  ]
}

Am I missing something or is there indeed a behavior change, and is it expected? Is it documented somewhere I didn’t find?

Thanks in advance!

Showing Posts 1 to 5

zachdaniel

zachdaniel

Creator of Ash

Can you try configuring in the action atomic_upgrade? false and see if that resolves the issue? This will help isolate the cause.

zachdaniel

zachdaniel

Creator of Ash

I have a suspicion of what the error is. Can you try main of ash and see if that resolves the issue?

zachdaniel

zachdaniel

Creator of Ash

Actually…I’m not so sure that is the issue :slight_smile: The action you’re showing couldn’t get an atomic upgrade anyway because of the function change. So the issue must be somewhere else.

zachdaniel

zachdaniel

Creator of Ash

Alright, try main now, I think I fixed the actual issue :slight_smile:

sheerlox

sheerlox OP

The first two solutions didn’t change anything, but your latest 2316b7b commit fixed the issue.

Thanks again Zach, you rock!

P.S. I’m still having issues believing you’re not a super-AI from the future, how do you do it :joy:

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews