Question: Powershell: -ErrorAction SilentlyContinue not working with Invoke-Expression

I am running this script
Invoke-Expression $expression -ErrorAction SilentlyContinue
The variable $expression may not  have a value sometimes. When It's empty, I get the error

Cannot bind argument to parameter 'Command' because it is null.

How can I avoid seeing the error? I want to execute Invoke-Expression regardless of $expression being empty, so an if statement checking that $expression has a value wouldn't work.



Answer:

First, the obligatory warning:

Invoke-Expression  (iex) should generally be avoided and used only as a last resort, due to its inherent security risks. Superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see this answer.


To add to Mathias' helpful answer and zett42's simpler alternative mentioned in a comment on the question (Invoke-Expression "$expression "):
These solutions silence only the case where $null or an empty string is passed to Invoke-Expression - which may well be your intent.
To also cover the case where all error output should be silenced - whether due to invalid input or due to valid input causing errors during execution - the following variation is needed:
# Silences *all* errors.
try { Invoke-Expression $expression 2>$null } catch { }

# Alternative:
# Silences *all* errors and additionally *ignores* all *non-terminating* errors, 
# i.e. not only silences them, but also prevents their recording in $Error.
# By executing inside & { ... }, the effect of setting $ErrorActionPreference is 
# transitory due to executing in a *child scope*.
# Note that this also means that $expression is evaluated in the child scope.
& { $ErrorActionPreference = 'Ignore'; Invoke-Expression $expression }

Note:

First command:

The common -ErrorAction parameter fundamentally only acts on non-terminating errors, whereas terminating ones (both statement- and script-terminating ones) must be handled with a try / catch / finally statement.

Passing $null or the empty string to Invoke-Expression causes an error during parameter binding (that is the, cmdlet itself is never invoked, because invalid arguments were passed), which in effect is a statement-terminating error - hence the need for try / catch.

The try / catch with the empty catch block additionally prevents script-terminating errors that result from the Invoke-Expression call from terminating your script too (e.g, if $expression contained something like 'throw "Fatal Error"'



Note that -ErrorAction SilentlyContinue was replaced with 2>$null in order to silence non-terminating errors (e.g., the error resulting from Get-ChildItem NoSuchDir), because - inexplicably - -ErrorAction is not effective with Invoke-Expression (even though the -ErrorVariable common parameter does work, for instance). See GitHub issue #19734.



Second command:

Setting the $ErrorActionPreference preference variable to Ignore causes all errors to be silenced, and additionally - for non-terminating errors only - prevents their recording in the automatic $Error variable.

If -ErrorAction worked in this case, -ErrorAction would have the same effect, but would act solely on non-terminating errors, as noted.  (This asymmetry between what should be equivalent mechanisms - preference variable vs. per-call common parameter, is one of the pitfalls of PowerShell's error handling - see GitHub issue #14819).


Unfortunately, (caught) terminating errors are invariably recorded in $Error as of PowerShell 7.3.4. From what I can tell, changing this in a future version has been green-lit a while ago, but is yet to be implemented:  see GitHub issue #3768

Using &, the call operator with a script block { ... } executes the enclosed statements in a child scope.

This causes $ErrorActionPreference = 'Ignore' to create a local copy of the preference variable, which automatically goes out of scope when the script block is exited, thereby implicitly restoring the previous value of $ErrorActionPreference.

However, this also means that the code executed by Invoke-Expression executes in that child scope.
If that is undesired, forgo the & { ... } enclosure and save and restore the previous $ErrorActionPreference value.