Dialyzer error: the guard clause can never succeed

The guard clause:

when _ :: number() === nil

can never succeed.

ElixirLS Dialyzer

I get the above dialyzer error for this line of code:

total_count: existing.aggregate.total_count + data["aggregate"]["total_count"] || 0,

Should I change it to Map.get(data["aggregate"], "total_count", 0) ?

I would think that the || 0 thing makes sure you will not have a nil but I’m not seeing enough context to say for sure.

This expands to:

case data["aggregate"]["total_count"] do
  x when x === false or x === nil -> 0
  x -> x
end

What dialyzer is telling you is that data["aggregate"]["total_count"] or later x is of type number(), so never nil (or false).

Your code is generally correct. The mismatch is why you think data["aggregate"]["total_count"] could be not a number, but dialyzer thinks the opposite.

It feels so weird because it means I cannot code defensively and add extra checks. Is there a way to make dialyzer less strict?

Or should I completely trust dialyzer and remove the || 0 ?

Dialyzer analyzes in the context of the code you have written, meaning that for the code you have currently there is no instance where that key can be nil, when you will have an actual path that makes that value nil, dialyzer will not complain anymore.

You don’t want to write defensive code anyway, there is no reason for random nils to be around the codebase unless you decide explicitly that you have a need for them.

You got this backwards. Dialyzer is telling you whether a contract that you declared is corresponding to what your code really does.

If you need the fallback value (in this case the integer zero) then by all means keep it but instruct Dialyzer that the return value is always an integer – meaning remove the nil clause because your code will not return nil.


As a broader discussion, if returning nil is an actual error then you should take care about raising it or tracking it somewhere and not just put a default value. That strongly depends on your business logic however. There’s no right or wrong way, it all depends on what is the business requirement for that piece of code.

It’s very strange but adding brackets seems to make the dialyzer error go away:

total_count: existing.aggregate.total_count + (data["aggregate"]["total_count"] || 0),

That doesn’t quite make sense to me.

+ has a higher precedence than || (see Operators — Elixir v1.12.3).

So adding explicit ordering these two would behave the same:

existing.aggregate.total_count + data["aggregate"]["total_count"] || 0
(existing.aggregate.total_count + data["aggregate"]["total_count"]) || 0

Given you’re doing some addition dialyzer correctly inferred that the left side of || will only ever be a number. You adding explicit parenthesis to change the order of operations resolved that and actually removed a bug.

2 Likes