Q:

Why is Logic Apps' if() expression executing both code paths simultaneously?

I am getting failures on a Logic App because the if() expression is executing both the true and false paths. The false path is going to fail if it executes which is why I put it in an if() in the first place.
The expression is:
if(empty(triggerBody()?['data']?['eta']), null, formatDateTime(triggerBody()?['data']?['eta'], 'yyyy-MM-dd'))

I've also tried:
if(equals(triggerBody()?['data']?['eta'], null), null, formatDateTime(triggerBody()?['data']?['eta'], 'yyyy-MM-dd'))

The data is null:
...
"data": {
    "eta": null,
...

I've tested this by swapping out formatDateTime() with a string like 'is not null'. When I do this I get the expected output (null) and no failure.
Update: Someone from the Logic Apps team suggested the following as an alternative:
@if(empty(triggerBody()?['data']?['eta']), null, formatDateTime(coalesce(triggerBody()?['data']?['eta'], '1999-01-01'), 'yyyy-MM-dd'))

The usage of coalesce() is suggested here because it returns the first non-null result. So in this way I can at least be assured of providing a value to formatDateTime().

A:

Why?  Because that's the way it's written. ;)
What you are seeing is the expected behavior of if() execution in a LogicApp.  Someone from the LogicApp team mentioned this (Channel 9?, sorry don't recall) and don't expect it to change any time soon.
If either case might cause a runtime error, you'll need to do it in two steps.

