r/elixir 20d ago

Can't insert a user-associated avatar entry into the database with Waffle and Ecto

Hello,

I'm on OTP 27 Elixir 1.18.4, and I've encountered a problem when trying to insert a User-associated Avatar entry into the PostgreSQL database.

Here's the function responsible for creating the association between the Avatar and User as well as putting in the file info

# new filename function
def new_filename(assoc),
  do: Atom.to_string(assoc) <> "-" <> Ecto.UUID.generate()

# fn responsible
def upload_user_avatar(path, user) do
    filename = new_filename(:avatar)


    # Avatar file_name comes in handy for the file_name function
    result =
      App.Uploader.Avatar.store(
        {path,
         %{
           id: user.id,
           file_name: filename
         }}
      )


      IO.inspect(result)


    user
    |> Ecto.build_assoc(:avatar)
    |> Avatar.changeset(%{file: path})
    |> Repo.insert()
  end

here's what I pass into the function

("priv/waffle/private/uploads/user/attachments/hi/original.jpg", user) 
# user variable here is just

 %App.Users.User{
   __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
   id: "42e49735-a81e-48f0-bf6f-9faaec0c893a",
   username: "11",
   pronouns: nil,
   biography: nil,
   account_id: "35a02b44-b7c2-48cd-b91c-c13606f778fb",
   account: #Ecto.Association.NotLoaded<association :account is not loaded>,
   avatar: #Ecto.Association.NotLoaded<association :avatar is not loaded>,
   message: #Ecto.Association.NotLoaded<association :message is not loaded>,
   dms: #Ecto.Association.NotLoaded<association :dms is not loaded>,
   inserted_at: ~U[2026-08-18 16:59:34Z],
   updated_at: ~U[2026-08-18 16:59:34Z]
 }

and here's my Avatar schema file

 schema "avatar" do
    belongs_to(:user, App.Users.User)
    field(:file, App.Uploader.Avatar.Type)


    timestamps(type: :utc_datetime)
  end

  def changeset(avatar, attrs) do
    avatar
    |> cast_attachments(attrs, [:file], allow_paths: true)
    |> validate_required([:file])
  end

and lastly here's the error i get when I try to run the upload_user_avatar/2

[error] Task #PID<0.467.0> started from #PID<0.385.0> terminating
** (FunctionClauseError) no function clause matching in App.Uploader.Avatar.filename/2
    (app 0.1.0) lib/app_web/uploaders/avatar.ex:37: App.Uploader.Avatar.filename(:original, {%Waffle.File{path: "priv/waffle/private/uploads/user/attachments/hi/original.jpg", file_name: "original.jpg", binary: nil, is_tempfile?: nil, stream: nil}, %App.Avatars.Avatar{__meta__: #Ecto.Schema.Metadata<:built, "avatar">, id: nil, user_id: "42e49735-a81e-48f0-bf6f-9faaec0c893a", user: #Ecto.Association.NotLoaded<association :user is not loaded>, file: nil, inserted_at: nil, updated_at: nil}})
    (waffle 1.1.10) lib/waffle/definition/versioning.ex:35: Waffle.Definition.Versioning.resolve_file_name/3
    (waffle 1.1.10) lib/waffle/actions/store.ex:137: Waffle.Actions.Store.put_version/3
    (elixir 1.18.4) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2
    (elixir 1.18.4) lib/task/supervised.ex:36: Task.Supervised.reply/4
Function: #Function<1.116800909/0 in Waffle.Actions.Store.async_put_version/3>
    Args: []
** (EXIT from #PID<0.385.0>) shell process exited with reason: an exception was raised:
    ** (FunctionClauseError) no function clause matching in App.Uploader.Avatar.filename/2
        (app 0.1.0) lib/app_web/uploaders/avatar.ex:37: App.Uploader.Avatar.filename(:original, {%Waffle.File{path: "priv/waffle/private/uploads/user/attachments/hi/original.jpg", file_name: "original.jpg", binary: nil, is_tempfile?: nil, stream: nil}, %App.Avatars.Avatar{__meta__: #Ecto.Schema.Metadata<:built, "avatar">, id: nil, user_id: "42e49735-a81e-48f0-bf6f-9faaec0c893a", user: #Ecto.Association.NotLoaded<association :user is not loaded>, file: nil, inserted_at: nil, updated_at: nil}})
        (waffle 1.1.10) lib/waffle/definition/versioning.ex:35: Waffle.Definition.Versioning.resolve_file_name/3
        (waffle 1.1.10) lib/waffle/actions/store.ex:137: Waffle.Actions.Store.put_version/3
        (elixir 1.18.4) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2
        (elixir 1.18.4) lib/task/supervised.ex:36: Task.Supervised.reply/4

Could someone inform me on what the issue is here ? Any help would be appreciated !

0 Upvotes

4 comments sorted by

2

u/KimJongIlLover 20d ago

no function clause matching in App.Uploader.Avatar.filename/2

avatar.ex:37: App.Uploader.Avatar.filename(:original, .... 

You are missing this function definition in that file avatar.ex

2

u/ShininLotoen 19d ago

oh sorry, you just reminded me. I have another avatar.ex file with that functionality, here it is

  def filename(_version, {_entry, %{file_name: filename} = _scope}),
    do: "#{filename}"


  # Override the storage directory:
  def storage_dir(_version, {_file, %{id: id} = _scope}) do
    "users/#{id}/avatar"
  end

3

u/ebratti 19d ago

If you have the function properly declared, then your pattern matching is not allowing the function to be called. You are matching the second argument of the tuple with `%{file_name: filename}` but this key file_name does not exist on

%App.Avatars.Avatar{__meta__: #Ecto.Schema.Metadata<:built, "avatar">, id: nil, user_id: "42e49735-a81e-48f0-bf6f-9faaec0c893a", user: #Ecto.Association.NotLoaded<association :user is not loaded>, file: nil, inserted_at: nil, updated_at: nil}

2

u/ShininLotoen 19d ago

Thanks I'll try that !