Weird error updating many_to_many association `no function clause matching in anonymous fn/2 in Ecto.Changeset.get_changed/6`

I cannot figure out what could be wrong here I copied the code directly from the many_to_many docs.

def highlight_verses(verse_ids, member) do
    verses = get_verses(verse_ids)

    loaded =
      member
      |> Repo.preload(:highlights)

    loaded
    |> Changeset.change()
    |> Changeset.put_assoc(:highlights, [verses | loaded.highlights])
    |> Repo.update()
  end

Here is the association

schema "members" do
    field :email, :string
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :naive_datetime
    field :first_name, :string
    field :last_name, :string
    field :accepted_terms_at, :naive_datetime
    field :accepted_privacy_at, :naive_datetime

    has_many :account_members, WithoutCeasing.Identity.AccountMember, on_delete: :delete_all
    has_many :accounts, through: [:account_members, :accounts]
    has_many :resources, WithoutCeasing.Content.Resource
    has_many :notes, WithoutCeasing.Content.Note

    many_to_many :highlights, WithoutCeasing.Bible.Verse,
      join_through: "highlights",
      on_replace: :delete

    timestamps()
  end

Any ideas?

Probably too obvious a question but still: are verses and member.highlights containing collections of the same structs?

Yes! Sorry here is the other side

schema "verses" do
    field :book, :string
    field :chapter, :integer
    field :verse, :integer

    has_many :verse_revisions, WithoutCeasing.Bible.VerseRevision

    many_to_many :members, WithoutCeasing.Identity.Member,
      join_through: "highlights",
      on_replace: :delete

    many_to_many :notes, WithoutCeasing.Content.Note,
      join_through: "verse_notes",
      on_replace: :delete

    many_to_many :resources, WithoutCeasing.Content.Resource,
      join_through: "verse_resources",
      on_replace: :delete
  end

Nothing immediately springs to mind, sorry. Have you tried IO.inspect-ing both verses and member.highlights before the Changeset.put_assoc call?

As far as I can tell they both look good. One is an empty array and the other is an array with one %Verse{} struct. Here is more of the stack trace:

** (FunctionClauseError) no function clause matching in anonymous fn/2 in Ecto.Changeset.get_changed/6
    (ecto 3.7.1) lib/ecto/changeset.ex:406: anonymous fn(%WithoutCeasing.Bible.Verse{__meta__: #Ecto.Schema.Metadata<:loaded, "verses">, book: "john", chapter: 12, id: 43012001, members: #Ecto.Association.NotLoaded<association :members is not loaded>, notes: #Ecto.Association.NotLoaded<association :notes is not loaded>, resources: #Ecto.Association.NotLoaded<association :resources is not loaded>, verse: 1, verse_revisions: #Ecto.Association.NotLoaded<association :verse_revisions is not loaded>}, {%{}, [], true}) in Ecto.Changeset.get_changed/6
    (elixir 1.12.0) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ecto 3.7.1) lib/ecto/changeset.ex:400: Ecto.Changeset.change/2
    (ecto 3.7.1) lib/ecto/changeset/relation.ex:202: Ecto.Changeset.Relation.do_change/4
    (ecto 3.7.1) lib/ecto/changeset/relation.ex:351: Ecto.Changeset.Relation.map_changes/9
    (ecto 3.7.1) lib/ecto/changeset/relation.ex:165: Ecto.Changeset.Relation.change/3
    (ecto 3.7.1) lib/ecto/changeset.ex:1222: Ecto.Changeset.put_change/7
    (ecto 3.7.1) lib/ecto/changeset.ex:1432: Ecto.Changeset.put_relation/5
    (without_ceasing 0.1.0) lib/without_ceasing/bible.ex:51: WithoutCeasing.Bible.highlight_verses/2

Can you show the exact offending line? Or re-repost a code snippet and point at line 51?

This is likely the problem - doing this results in a list where the first element is a list of Verse structs.

The example in put_assoc uses [ | ], but to add a single comment.

verses ++ loaded.highlights would give a flat list.

1 Like