Edward Gizbreht
Edward Gizbreht

Reputation: 306

Ecto ArgumentError: "comparison with nil is forbidden" when querying for null field in Phoenix/Elixir

I'm getting an error in my Phoenix/Elixir application when searching for records where a DateTime field is null. Here's the error:

** (ArgumentError) comparison with nil is forbidden as it is unsafe. If you want to check if a value is nil, use is_nil/1 instead
    (ecto 3.12.4) lib/ecto/query/builder.ex:1188: Ecto.Query.Builder.not_nil!/1

Here's my schema:

schema "instances" do
  field :id, :string, primary_key: true
  field :tournament_id, :string
  field :config_id, :string
  field :occurrence_id, :string
  field :ended_at, :utc_datetime, default: nil
  timestamps()
end

And here's the query that's failing:

def get_active_instance(tournament_id, config_id) do
  Instance
  |> where(
    [i],
    i.tournament_id == ^tournament_id and
    i.config_id == ^config_id and
    is_nil(i.ended_at)
  )
  |> Repo.one()
  |> case do
    nil -> {:error, :no_active_tournament_instance}
    instance -> {:ok, instance}
  end
end

I'm trying to find instances where ended_at is null (indicating an active instance), along with matching tournament_id and config_id. The schema has the field defaulting to nil, but I'm getting this comparison error. What's the correct way to query for null fields in Ecto? The error message suggested using is_nil/1, but I still get the same error. Environment:

Elixir 1.17.3 Phoenix LiveView 1.0.0 Ecto 3.12.4

Upvotes: 0

Views: 97

Answers (1)

Edward Gizbreht
Edward Gizbreht

Reputation: 306

The issue happens because Ecto doesn’t allow direct comparisons with nil, which can occur if your inputs (tournament_id or config_id) are nil. I added a guard clause to fix it to check that both inputs are valid strings before running the query. Here's an example:

def get_active_instance(tournament_id, config_id)
      when is_binary(tournament_id) and is_binary(config_id) do
    Instance
    |> where(
      [i],
      i.tournament_id == ^tournament_id and
        i.config_id == ^config_id and
        is_nil(i.ended_at)
    )
    |> Repo.one()
    |> case do
      nil -> {:error, :no_active_tournament_instance}
      instance -> {:ok, instance}
    end
  end

Upvotes: 0

Related Questions