7

I want all of the functions in my module to default to $ErrorActionPreference = 'Stop'. Is it possible without modifying all the functions?

I have a file per function.

mark
  • 59,016
  • 79
  • 296
  • 580
  • Have you tried using ```[CmdletBinding()]``` and just running the function with ```-ErrorAction Stop```? This would give you the behavior you are looking for, without doing any adjusting of the global variable ```$ErrorActionPreference```? As mentioned here on StackOverflow: https://stackoverflow.com/questions/5950760/emulating-erroraction-in-custom-powershell-function or here on Microsoft Docs: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-6 -- either way, CmdletBinding autoenables additional params – ScriptAutomate Apr 24 '19 at 23:15
  • 1
    I do not want to depend on this behaviour having been set externally when the command is run. I want it to be the default for my functions. – mark Apr 24 '19 at 23:19

2 Answers2

15

Assuming that your module is a script module, i.e., implemented in PowerShell code:

Important:

  • Modules have their own stack of scopes that is independent of the scopes of non-module code (and other modules'). While this provides isolation from the caller's environment that is generally useful, it also means that the caller's $ErrorActionPreference value never takes effect for script-module functions (unless you directly run from the global scope, which modules also see) - but it does so for compiled cmdlets. This highly problematic behavior is discussed in this GitHub issue.

  • Even though you therefore currently cannot control a script module's error behavior from the caller by way of $ErrorActionPreference, by setting (overriding) $ErrorActionPreference in your module you're closing that door permanently.

  • However, using the -ErrorAction common parameter for a specific call instead of the $ErrorActionPreference preference variable will still override your module-global $ErrorActionPreference value, because, behind the scenes, PowerShell translates the -ErrorAction argument to a function-local $ErrorActionPreference variable with that value.

  • The -ErrorAction and $ErrorActionPreference mechanisms are plagued by inconsistencies and obscure behaviors - this GitHub docs issue provides a comprehensive overview of PowerShell's error handling.

I want all of the functions in my module to default to $ErrorActionPreference = 'Stop'. Is it possible without modifying all the functions?

Yes - simply place $ErrorActionPreference = 'Stop' in your RootModule *.psm1 file's top-level code. (The RootModule entry of a module's manifest file (*.psd1) specifies a module's main module - see the docs).

  • If you don't have a RootModule entry that references a .psm1 file, simply create a .psm1 file in your module folder (typically named for the enclosing module) with content $ErrorActionPreference = 'Stop', and reference it from the RootModule entry - see this answer for background information.

Unless overridden by the caller by using the common -ErrorAction parameter when calling your module's functions (assuming they are advanced functions), your module's top-level $ErrorActionPreference value will be in effect for all of your module's functions, except if your function directly emits a statement-terminating error[1], in which case it is the caller's $ErrorActionPreference value that matters.


If your module is a binary module, i.e., exports compiled cmdlets (typically implemented in C#):

Compiled cmdlets don't have their own scope - they run in the caller's scope. It is therefore the caller's $ErrorActionPreference that matters, which can be overridden on a per-call basis with common parameter -ErrorAction, but only for non-terminating errors.

As with advanced functions in script modules, directly emitted statement-terminating errors[1] are always subject to the caller's $ErrorActionPreference value, even if -ErrorAction is used. (Note that binary cmdlets do not emit script-terminating errors).


[1] Statement-terminating errors occur in the following scenarios:

  • Directly, to abort execution of the enclosing cmdlet or advanced function/script:

    • When a binary cmdlet encounters a severe error that prevents it from continuing, it reports such an error with the ThrowTerminatingError() method (or just throws an exception).

    • An advanced PowerShell function/script would similarly have to use $PSCmdlet.ThrowTerminatingError(), which, however, is rare in practice; the typically used Throw statement creates a script-terminating error instead, which by default also terminates the entire thread, i.e., also terminates the caller and all its callers.

  • Indirectly, in PowerShell code:

    • When an expression causes a runtime error, such as 1 / 0 or 'a,b' -split ',', 'NotAnInt'.

    • When a .NET method call throws an exception, such as [int]::Parse('NotAnInt')

    • When, inside an advanced function/script, another cmdlet or advanced function / script is called that itself directly emits a statement-terminating error.

    • Note that advanced functions/scripts cannot relay statement-terminating errors as such:

      • By default (with $ErrorActionPreference containing 'Continue', possibly just in the local scope) the expression's / other command's terminating error effectively becomes a non-terminating error from the caller's perspective.

      • With $ErrorActionPreference set to 'Stop', the originally statement-terminating error is promoted to a script-terminating error.

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

It looks like you can do this with explicit default parameter setting in the $PSDefaultParameterValues session-wide preference variable.

This includes the ability to set the ErrorAction Common Parameter default for specific functions. Keep in mind, you would want to append the current value of $PSDefaultParameterValues since it uses a hash table, and other functions may currently have defaults set in the current session. This would also mean that [CmdletBinding()] needs to be included in every function that is being given this default value.

$PSDefaultParameterValues is a hash table, so you would want to modify in a fashion like so:

$PSDefaultParameterValues += @{
    "Get-Function:ErrorAction"="Stop"
    "Get-Command:ErrorAction"="Stop"
    "Get-MyFunction*:ErrorAction"="Stop"
}

or

$PSDefaultParameterValues.add("Get-Function:ErrorAction","Stop")

Wildcards are accepted in function/cmdlet names, which may make it easier to get all of your functions in one line if you have a unique noun prefix naming scheme for imports from your module (though, this would include any other functions/cmdlets if any are imported into a session that include the same naming prefix).

ScriptAutomate
  • 980
  • 9
  • 17
  • 1
    This seems to belong to the profile script, which is not a good idea. I want it to be the feature of the module - every function in it runs with EA = 'Stop' as the default. – mark Apr 26 '19 at 22:35