Multi.run returns an error in Phoenix 1.5.1 (Ecto 3.4.3)

Friends I need your help again in relation to migration to latest Phoenix 1.5.1. This code was working fine in 1.4.x, now causes an error:

    |> Stream.map(fn move ->
      multi_result =
        Multi.new()
        |> Multi.run(:move, fn _move  -> # this is the line causing the error
          Repo.insert(
            Move.changeset(%Move{}, %{
              source: source,
              class: move.class,
              ref: ref,
              item_id: move.item_id,
              quantity: move.quantity,
              line_num: move.line_num,
              docdate: move.docdate,
              note: move.note,
              create_date: move.create_date,
              card_name: move.card_name,
              card_code: move.card_code
            })
          )
        end)
        |> Repo.transaction()
        |> case do
          {:ok,
           %{
             move: move
           }} ->
            move

          {:error, :move, reason, _changes} ->
            -1
        end

      {move.line_num, multi_result}
    end)

The error message:

[error] #PID<0.19217.0> running StockWeb.Endpoint (connection #PID<0.19216.0>, stream id 1) terminated
Server: my.server:80 (http)
Request: POST /api/moves
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in anonymous fn/2 in Stock.Moves.create_moves_in/3
        (stock 0.0.1) lib/stock/moves/moves.ex:194: anonymous fn(Stock.Repo, %{}) in Stock.Moves.create_moves_in/3
        (ecto 3.4.3) lib/ecto/multi.ex:585: Ecto.Multi.apply_operation/5
        (elixir 1.10.3) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ecto 3.4.3) lib/ecto/multi.ex:569: anonymous fn/5 in Ecto.Multi.apply_operations/5
        (ecto_sql 3.4.3) lib/ecto/adapters/sql.ex:875: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
        (db_connection 2.2.2) lib/db_connection.ex:1427: DBConnection.run_transaction/4
        (ecto 3.4.3) lib/ecto/repo/transaction.ex:20: Ecto.Repo.Transaction.transaction/4
        (stock 0.0.1) lib/stock/moves/moves.ex:211: anonymous fn/3 in Stock.Moves.create_moves_in/3
        (elixir 1.10.3) lib/stream.ex:572: anonymous fn/4 in Stream.map/2
        (elixir 1.10.3) lib/enum.ex:3686: Enumerable.List.reduce/3
        (elixir 1.10.3) lib/stream.ex:1609: Enumerable.Stream.do_each/4
        (elixir 1.10.3) lib/enum.ex:3383: Enum.into/4
        (stock 0.0.1) lib/stock_web/controllers/move_controller.ex:144: StockWeb.MoveController.create/2
        (stock 0.0.1) lib/stock_web/controllers/move_controller.ex:1: StockWeb.MoveController.action/2
        (stock 0.0.1) lib/stock_web/controllers/move_controller.ex:1: StockWeb.MoveController.phoenix_controller_pipeline/2
        (phoenix 1.5.1) lib/phoenix/router.ex:352: Phoenix.Router.__call__/2
        (stock 0.0.1) lib/stock_web/endpoint.ex:1: StockWeb.Endpoint.plug_builder_call/2
        (stock 0.0.1) lib/plug/debugger.ex:132: StockWeb.Endpoint."call (overridable 3)"/2
        (stock 0.0.1) lib/stock_web/endpoint.ex:1: StockWeb.Endpoint.call/2
        (phoenix 1.5.1) lib/phoenix/endpoint/cowboy2_handler.ex:64: Phoenix.Endpoint.Cowboy2Handler.init/4

I have looked it up in the docs but could not find the reason for this. Your help is highly appreciated.

Multi.run documentation clearly shows you need 3 parameters at least.

This is not about Phoenix, your Ecto version got bumped.

1 Like

Between Ecto 2.1.x and 3.4.x the number of arguments needed to be passed to the function have not changed. They are exactly the same run(multi, name, run). At least as can been seen in the documents.

Solution:

Changing this:

|> Multi.run(:move, fn _move ->

to:

|> Multi.run(:move, fn _move, changes ->

1 Like

I should stop replying on the phone. :003:

Yes, this is what I wanted to write after: that the closure’s expected arguments have changed. Glad you found it and fixed it. :slight_smile:

2 Likes

I have the following Multi but it throws an error. I’m running on Phoenix 1.6.15 with Ecto 3.9.2. Not sure what I’m doing wrong…

Code:

    result =
        Multi.new()
        |> Multi.run(:line_items, fn _, {:ok, line_items} ->
          Orders.update_line_items_state(order)
          {:ok, line_items}
        end)
        |> Multi.run(:approve_order, fn params, {:ok, _changes} ->
          Repo.update(
            Order.changeset(order, %{
              state: "confirmed",
              total: Orders.calculate_order_total(params.line_items)
            })
          )
        end)
        |> Multi.run(:payment, fn _, {:ok, _changes} ->
          create_payment(%{
            payment_method: "credit",
            order_id: order.id,
            amount: amount,
            payment_state: "approved"
          })
        end)
        |> Repo.transaction()

Error:

** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in anonymous fn/2 in......
        (ecto 3.9.4) lib/ecto/multi.ex:832: Ecto.Multi.apply_operation/5
        (elixir 1.14.2) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ecto 3.9.4) lib/ecto/multi.ex:806: anonymous fn/5 in Ecto.Multi.apply_operations/5
        (ecto_sql 3.9.2) lib/ecto/adapters/sql.ex:1203: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
        (db_connection 2.4.3) lib/db_connection.ex:1611: DBConnection.run_transaction/4
        (ecto 3.9.4) lib/ecto/repo/transaction.ex:18: Ecto.Repo.Transaction.transaction/4

Need some help to fix this issue…