PowerShell is a versatile scripting language used for automation, configuration management, and administrative tasks. Like any programming or scripting language, errors can occur during script execution, especially when dealing with external systems, files, or user input. Proper error handling is essential to ensure scripts fail gracefully, offer meaningful feedback, and recover when possible.
In this article, we’ll explore common error handling techniques in PowerShell to help you build robust, efficient, and reliable scripts.
Understanding PowerShell Error Types
Before diving into error handling techniques, let’s go over the two types of errors in PowerShell:
- Non-Terminating Errors: These errors occur but do not stop the execution of the script. PowerShell simply logs the error and moves on to the next command. For example, when a file is not found during a Get-Item command, the script continues.
- Terminating Errors: These errors are critical and cause the script to stop execution. Examples include syntax errors or exceptions that prevent further commands from running.
By default, PowerShell treats most errors as non-terminating unless explicitly told otherwise.
PowerShell Error Handling Best Practices
When building out error handling mechanisms in your PowerShell scripts, keep these best practices in mind to ensure your scripts are robust and efficient:
- Anticipate errors: Consider when and where potential error scenarios may occur when writing scripts and utilize the techniques here to capture relevant error information.
- Use meaningful error messages: Provide clear, descriptive error messages that will inform the user of necessary information to resolve issues.
- Log errors: Record errors to a log file so they can be retrieved and referenced at a later date and compared to other script executions for failure history.
- Test error handling: Verify that error handling mechanisms work as expected. Like all other aspects of script writing, testing is important to ensure that everything works as expected and will generate the intended information in the event of an error.
Using Try/Catch/Finally for Error Handling
One of the most powerful and structured ways to handle errors in PowerShell is through try, catch, and finally blocks, similar to many other programming languages like C#. The try block contains code that might throw an error. If a terminating error occurs, the catch block runs to handle it, and the finally block runs afterward regardless of whether an error occurred.
Syntax:
try {
# Code that may throw an exception
}
catch {
# Handle errors here
}
finally {
# Code that will run regardless of whether an error occurred or not
}
- try block: Contains the code that may throw errors.
- catch block: Handles any terminating errors that occur in the try block.
- finally block: Executes after the try and catch blocks, regardless of whether an error occurred. This is useful for cleanup tasks (e.g., closing files or connections).
Example:
In this example, the Get-File cmdlet fails because it does not exist, triggering the catch block to display the error to the console. The finally block then runs regardless of whether the try block fails, displaying another message to the console.
You can even filter the catch block by specific error types (e.g., catch [System.IO.IOException]), which allows you to handle specific exceptions in different ways depending on the type of error. This is especially useful for managing errors with custom handling based on the context or severity of the exception.
Syntax:
try {
# Code that may throw an exception
}
catch [ExceptionType] {
# Handle specific type of error
}
catch {
# Handle any other errors
}
- try block: Contains the code that might throw errors
- catch [ExceptionType] block: Handles specific types of errors by filtering for that exception type. For example, [System.Management.Automation.RuntimeException] would handle runtime errors only. This allows the script to respond differently to specific errors.
- catch block (general): Catches all other terminating errors not handled by the specific catch [ExceptionType] block(s). Acts as a fallback for an unanticipated error, providing an additional layer of error management.
Example:
This example filters the first catch block to handle only RuntimeException errors, allowing other errors to fall through to the general catch block. This setup can be helpful for applying specific responses to different error types.
Handling Non-Terminating Errors with -ErrorAction
PowerShell cmdlets often generate non-terminating errors by default, which are errors that log a message and allow the script to continue. While this behavior can be helpful for processes that need to keep running despite minor issues, it also means that the script won’t automatically break into a catch block for error handling. To handle these errors effectively, you can change the behavior using the -ErrorAction parameter.
-ErrorAction can be set to one of the following values:
- Continue (default): Logs the error and continues execution.
- Stop: Converts non-terminating errors into terminating errors, triggering the catch block.
- SilentlyContinue: Suppresses error messages and continues.
- Ignore: Ignores the error, similar to SilentlyContinue but without even updating the $Error variable.
Example:
By setting -ErrorAction to Stop, you can ensure non-terminating errors are treated as terminating, allowing you to handle them in the catch block.
The $Error Variable
PowerShell maintains a global array $Error that stores recent errors encountered in the session. You can use this array to inspect the error details even after they occur. As new errors are generated, they are added to the beginning of the array, pushing older errors down the list. By default, the $Error array holds up to 256 errors, although this can be configured by updating the $MaximumErrorCount variable.
- $Error[0]: Contains the details of the most recent error.
- $Error[1], $Error[2], ...: Contains older errors in order of occurrence.
- $Error.Count: Returns the total number of errors stored in the $Error array.
- $Error.Clear(): Clears the $Error array, removing all stored errors.
Each error in $Error contains rich information, allowing you to dig deeper into what went wrong. Here are a few useful properties that can be referenced:
- Exception: Provides the exception object, which contains the error message and inner exceptions.
- CategoryInfo: A structured category for the error, including the category and reason.
- FullyQualifiedErrorId: A unique identifier for the error, which can be useful for filtering specific error types.
- InvocationInfo: Contains information about the command that triggered the error, such as the line number and script name.
Example:
In this example, after encountering an error, we can retrieve its details using $Error[0], including some of the additional properties, and use that in a custom error message to be displayed.
Using Throw for Custom Exceptions
You can use throw to create your own terminating errors in PowerShell. This is useful for enforcing custom error conditions or stopping execution if specific criteria are not met.
You might use throw in various scenarios, such as:
- Input Validation: Checking that necessary variables or parameters are provided.
- Environment Checks: Ensuring that specific resources (files, folders, network connections) are available.
- Custom Error Handling: Creating errors with descriptive messages to inform users about specific failure points in the script.
Example:
When the throw statement is encountered, a terminating error is generated, and the custom error message is displayed.
The throw statement becomes even more powerful when used within a try/catch block, as this allows you to gracefully handle the error without completely stopping the script.
Setting the $ErrorActionPreference Variable
The $ErrorActionPreference variable globally controls how PowerShell handles non-terminating errors. You can set this variable to the same values as -ErrorAction, which we discussed above, allowing you to control the error behavior of all commands in your script. By setting it to Stop, all non-terminating errors will be treated as terminating, simplifying error handling. Use this with caution though, as it can cause the script to stop execution for all non-terminating errors.
To modify the value of the $ErrorActionPreference variable:
When to Use $ErrorActionPreference:
- For Debugging: Setting $ErrorActionPreference to "Stop" can be useful when troubleshooting a script to ensure no errors are ignored.
- In Critical Scripts: If you want to enforce strict error handling (for example, in scripts with critical operations), "Stop" can help ensure any unexpected issue is immediately addressed.
- With Caution: Only use it in sections of your script where halting execution on any error is desired.
Logging Errors
Logging errors to a log file in PowerShell is essential for debugging, auditing, and tracking issues that occur during script execution. Having a detailed error log helps you understand what went wrong, where, and when, making it easier to troubleshoot and correct errors, especially in long or complex scripts. PowerShell provides several ways to log errors effectively.
PowerShell’s Start-Transcript cmdlet allows you to capture the entire session’s output, including errors, and save it to a log file. This method is straightforward and logs everything displayed in the console, making it particularly useful for capturing both output and errors in a single file.
- Start-Transcript begins the logging session, recording all output and errors.
- Stop-Transcript ends the logging session.
A custom logging function gives you more flexibility to control when and how to log messages. This approach is helpful if you only want to log specific events or errors. This method also allows you to add things like timestamps to the message and gives you more granular control over your log entries. However, this method does require building out a custom function to handle the logging, so is more complex than the Start-Transcript method. Here is an example of a custom Log-Message function:
You can call your custom logging function by referencing it by name and passing in the required parameters.
Another simple approach to capturing error messages to a log file is to utilize the Write-Error cmdlet along with redirection to send the error message to your log file location. This method works well for simple error capture where you do not require additional formatting or information.
Logging errors systematically can make troubleshooting PowerShell scripts much easier, especially in production environments where direct access to the console may not always be available and scripts are often running unattended.
Using PowerShell Error Handling
Effective error handling in PowerShell is crucial for writing reliable and maintainable scripts. Using techniques like try/catch, -ErrorAction, and the $Error variable, you can ensure your scripts handle unexpected situations gracefully and provide helpful feedback to users. By incorporating these techniques into your PowerShell scripts, you can reduce the likelihood of failures and make your automation solutions more robust.