Unlike other programming languages, the PowerShell scripting language has two types of error scenarios: terminating and non-terminating. Both of these types of errors are "bad," but by classifying them differently, the scripter can handle them differently. This distinction is important if you want to stop or exit a PowerShell script when it errors.

Non-terminating errors

For example, let's say I've got a script that checks to see whether a file exists or not. I'll call it ErrorExample.ps1. This script uses Test-Path to confirm whether a file exists. If it doesn't, it will return an error via the Write-Error cmdlet. This error will be non-terminating because Write-Error always returns non-terminating errors.

if (-not (Test-Path -Path C:\DoesNotExist.txt)) {
    Write-Error 'The file does not exist'
} else {
    Write-Host 'The file does exist'
}
Write-Host 'Continuing script regardless if file exists or not...'

The script will continue executing code whether or not that file exists as shown below.

Non terminating error

Non terminating error

This is a non-terminating error example because Write-Error did not terminate the script at line two. Instead, it returned the error to the console and kept going. This scenario sometimes isn't desirable. Perhaps that file you're checking for is critical to the success of the lines below it. In that case, you'd want the script to stop execution completely. You'd know that if that file doesn't exist, the rest of the script isn't going to work.

Terminating errors

Terminating errors are the second type of error in PowerShell. We can think of terminating errors as exceptions. Exceptions are either errors that terminate a script completely or ones PowerShell "throws" into a catch block to handle the error and perform whatever necessary actions after throwing the exception.

One way a scripter can invoke a terminating error is by using the throw keyword. This PowerShell construct creates a terminating error while also throwing an exception. Using the example above, let's say that file is critical and we'd like to stop script execution if it doesn't exist. We could replace Write-Error with throw instead.

if (-not (Test-Path -Path C:\DoesNotExist.txt)) {
    throw 'The file does not exist'
} else {
    Write-Host 'The file does exist'
}

Write-Host 'Continuing script regardless if file exists or not...'

Notice now that line seven doesn't run; throw has stopped the script at line two.

Terminating error

Terminating error

Turning non-terminating into terminating errors

It's sometimes hard to replace all Write-Error references and other actions that produce non-terminating errors with throw statements. Instead, you can keep all of those non-terminating error-producing commands in your scripts and "convert" them to terminating errors.

If you'd like to turn all non-terminating errors into terminating ones, you can perform a global action change by setting the $ErrorActionPreference variable to Stop. This automatic variable controls non-terminating error behavior. By setting $ErrorActionPreference to Stop, you're telling PowerShell to treat all instances where a non-terminating error occurs as terminating. This action applies across the board.

However, maybe you'd rather not change all non-terminating errors to terminating ones and pick certain ones instead. Using the common parameter ErrorAction applied to all cmdlets and advanced functions, you can make this happen.

In our example again, I can actually "override" the Write-Error cmdlet's behavior by forcing it to throw a terminating error. I'll use the ErrorAction parameter and set it to Stop.

Subscribe to 4sysops newsletter!

Write-Error 'The file does not exist' -ErrorAction Stop

When the script runs now, you'll see that it stops execution without using the throw keyword. You can use the ErrorAction parameter on every cmdlet and advanced function in PowerShell. It is a great way to choose which commands should stop script execution and which ones should not. Depending on the severity and dependencies further down in the script, this may or not may be necessary.

avataravatar
14 Comments
  1. Andrey Voronin (Rank 1) 5 years ago

    IMHO, it is good to turn all non-terminating errors into terminating ones and process with try{} catch {}.

  2. Justin 4 years ago

    The first example, under “Non-terminating errors” doesn’t work. The script does NOT continue running.

    • @Justin

      The article of Adam is correct.

      Can you share the lines of code (not the whole script) that are not working as expected on your computer?

  3. David 4 years ago

    I always see these examples, but none go on to tell us how to suppress the error message that Throw produces. I would like my error message to be the ONLY error message that gets outputted and to terminate the script there and then.

    • @David

      You can achieve that with the try/catch statements.
      The try statement suppresses all output.
      Then inside the catch statement, you can choose which part of the error you want to display.

      try{
          throw 'My Custom Error Message'
      }
      catch{
          $error[0].Exception
      }
      

      $error is an array with all former exceptions.
      $Error[0] is the latest one.
      Each error has several properties.

      $error[0]|Get-Member -MemberType Properties

      I choose in my example to display the Exception property because this is probably what you want, but you can display any of them.

    • Leos Marek (Rank 4) 4 years ago

      This works perfectly for me

      try
      {
      blabla
      
      }
      catch
      {
      "$_"
      }

      The only thing I get is:

      The term 'blabla' is not recognized as the name of a cmdlet, function, script fi
      le, or operable program. Check the spelling of the name, or if a path was includ
      ed, verify that the path is correct and try again.

  4. David 4 years ago

    Hello again,

    Thanks for responding, I have been “Trying” to use the Try/Catch brackets, however I have not seen the Throw command been used within the Try brackets before.

    I originally had this:

    Try { $users = Get-ADUser -Filter * -Properties proxyAddresses -ErrorAction Stop }
    Catch { Throw "This script requires AD module be loaded, try again after it is."}

    And it threw my error, along with PS jargon, but at least it terminated the script. So I tried as you suggested:

    Try {  
      $users = Get-ADUser -Filter * -Properties proxyAddresses -ErrorAction Stop 
      Throw "This script requires AD module be loaded, try again after it is."
    }
    Catch { }

    And I got no output at all, except that I know it did not terminate the script, it continued.

    I ended up finding a different, however longer, way of doing it:

    Try { $users = Get-ADUser -Filter * -Properties proxyAddresses -ErrorAction Stop }
    Catch { write-host "This script requires AD module be loaded, try again after it is."; Exit }

    I’m open to seeing a more concise way of it, but could not as yet.

    PS. I’m not really sure how to use your code brackets, but gave it a shot using the provided toolbar, haha.

    • @David

      The Try/Catch statement is working like this:

      Let’s TRY something, and if it’s throwing an error,
      let’s CATCH the error to see how we will handle it.

      But your third example shows that you already understood this principle.

      Just an additional information:

      Don’t assume that there is only one possible error.
      There are many ways something can go wrong.
      Usually, I catch known errors and return the original exception when unknown errors are raised.

      In your example this would give something like:

      Try {
          $users = Get-ADUser -Filter * -Properties proxyAddresses -ErrorAction Stop
      }
      
      Catch [System.Management.Automation.CommandNotFoundException]{
          write-host 'This script requires AD module be loaded, try again after it is.'
          Exit
      }
      
      catch {
          $Error[0].Exception
          Exit
      }

  5. Travis Drake 4 years ago

    David,

    There really isn’t a good way to write error output without powershell dumping the context with the error. I am assuming that is what you mean when asking how to suppress the error message from powershell that throw produces.

    Unfortunately powershell wraps all exceptions (throw ‘x’ produces an exception) in ErrorRecords, which come along with the PS Jargon you mentioned. Powershell itself isn’t outputting that jargon, it is actually your shell choosing what to do with an ErrorRecord that bubbles up that causes the rest of the jargon to show up in addition to your throw message. Even Write-Error will still bubble up an error record, which your shell will dump that jargon for.

    This is normal and expected in powershell, and the right way to do what you are doing is the first way in your post (although you should probably filter on the error, as there may have been many reasons Get-ADUser could fail, not just because the commandlets were not loaded).

    avataravatar
  6. Andreas 2 weeks ago

    You should always set the $ErrorActionPreference to “Stop”.

    The distinction between non-terminating and terminating errors is the source of very many errors on itself. Because in almost all cases, the script is in an unexpected state after an error: the developer just does not remember to check for all possible errors. And letting software run in a state that the developer did not expect is always a bad idea.

    I have seen this happen very often: some batch encounters an error and just keeps doing “something”. The work to clean up this mess is almost always much worse than having a clear error in the first place.

    • I would respectively disagree – it’s always a matter of perspective and usage. Depending on the script, setting ErrorActionPreference to Stop can absolutely cause all kinds of noise, bloated problems.

      However, for troubleshooting purposes, sometimes it does help to set EA to Stop.. it’s very circumstantial.

      David F.

      • Andreas 1 week ago

        There is a reason that proper programming languages do not have the concept of non-terminating errors: it leads to unforeseeable results too often. Because errors tend to occur where nobody is expecting them.

        During 20 years of development and administration, I have met only about a handful of edge-cases where I actually used an empty catch-block (and commented the reason).
        But I have seen a gazillion scripts ignoring errors and causing real damage afterwards. E.g. one of those scripts had the task of cleaning up a directory. The dev ignored errors, since the script could encounter files in use and should just leave them. But one time it failed in determining the path itself and continued with $null as a value. The developer did not expect issues there. But now, his script deleted everything in the default-path, which was the user’s home-folder.

        A very good practice in software development (aside from gaming) is to fail as early as possible. This applies to script development as well.

        And if a developer does really have one of the edge-cases at his hands, where he wants to continue, he can always decide to do so by using an empty catch-block. But this should only be done after careful consideration and not as default-behaviour.

        Ignoring errors by default was just a stupid decision by MS.

  7. But there’s a huge difference between professional programmers and scripting admins. Script writers aren’t always looking at idempotency. And as one of the scripting admins.. if I’m doing something that starts deleting things, I do put in lots of checks, and force error checking.

    But, bad script design is bad script design 🙂

    David F.

Leave a reply

Please enclose code in pre tags

Your email address will not be published.

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account