Constraint error on struct delete while using delete_all option

Hello, I’m getting the following error while running a unit test that deletes an “artist” entity that has “tag” associations (the desired behavior is for the associations to be deleted when the parent is deleted).

** (Ecto.ConstraintError) constraint error when attempting to delete struct:

         * artist_tags_artist_id_fkey (foreign_key_constraint)

     If you would like to stop this constraint violation from raising an
     exception and instead add it as an error to your changeset, please
     call `foreign_key_constraint/3` on your changeset with the constraint
     `:name` as an option.

     The changeset has not defined any constraint.

     code: assert {:ok, %Artist{}} = Artists.delete_artist(artist)
     stacktrace:
       (ecto 3.2.5) lib/ecto/repo/schema.ex:689: anonymous fn/4 in Ecto.Repo.Schema.constraints_to_errors/3
       (elixir 1.10.0) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
       (ecto 3.2.5) lib/ecto/repo/schema.ex:674: Ecto.Repo.Schema.constraints_to_errors/3
       (ecto 3.2.5) lib/ecto/repo/schema.ex:451: anonymous fn/10 in Ecto.Repo.Schema.do_delete/4
       test/narktar/artists_test.exs:75: (test)

This is the migration used to create the join-table “artist_tags” where I use on_delete: :delete_all

def change do
    create table(:artist_tags) do
      add :artist_id, references(:artists, on_delete: :delete_all), null: false
      add :tag_id, references(:tags, on_delete: :delete_all), null: false

      timestamps()

    end
    create unique_index(:artist_tags, [:artist_id, :tag_id])
  end

I verified there is CASCADE ON DELETE on the foreign key constraint in the Postgres database, and have no problems deleting artists (and their associated tags) manually with queries against Postgres.

I feel Iike I might be missing something simple and any help would be appreciated. I can also post any more details regarding the parent schemas if that would be useful.

Can you show the code of both Ecto schema modules (artist and tag)?

Sure, here are the artist and tag schemas. I also have a schema for the join relationship that I’ll include as well.

defmodule Narktar.Artists.Artist do
  use Ecto.Schema
  use Arc.Ecto.Schema
  import Ecto.Changeset
  alias Narktar.Artists.Tag
  alias Narktar.Repo

  schema "artists" do
    field :description, :string
    field :image_path, Narktar.Image.Type
    field :like_count, :integer
    field :members, {:array, :string}
    field :name, :string
    field :uuid, :string
    field :urls, {:array, :string}
    many_to_many :tags, Tag, join_through: Narktar.Artists.ArtistTag, on_replace: :delete

    timestamps()
  end

  @doc false
  def changeset(artist, attrs) do
    artist
    |> Map.update(:uuid, Ecto.UUID.generate, fn val -> val || Ecto.UUID.generate end)
    |> cast(attrs, [:name, :description, :members, :urls, :like_count])
    |> cast_attachments(attrs, [:image_path])
    |> put_assoc(:tags, parse_tags(attrs))
    |> validate_required([:name, :uuid, :like_count])
  end

  defp parse_tags(attrs) do
    (attrs["tags"] || "")
    |> String.split(",")
    |> Enum.map(&String.trim/1)
    |> Enum.reject(& &1 == "")
    |> Enum.map(&get_or_insert_tag/1)
  end

  defp get_or_insert_tag(name) do
    Repo.get_by(Narktar.Artists.Tag, name: name) ||
      Repo.insert!(%Tag{name: name})
  end

end
defmodule Narktar.Artists.Tag do
  use Ecto.Schema
  import Ecto.Changeset
  alias Narktar.Artists.Artist

  schema "tags" do
    field :name, :string
    many_to_many :artists, Artist, join_through: Narktar.Artists.ArtistTag
    timestamps()
  end

  @doc false
  def changeset(tag, attrs) do
    tag
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end
defmodule Narktar.Artists.ArtistTag do
  use Ecto.Schema

  schema "artist_tags" do
    belongs_to :artist, Narktar.Artists.Artist
    belongs_to :tag, Narktar.Artists.Tag
    timestamps()
  end
end