Proper error handling is absolutely critical when developing PowerShell scripts, especially for production environments. One of the most powerful error handling techniques in PowerShell is throwing exceptions with custom messages.
Over my years working with PowerShell, I’ve found that a PowerShell script should show proper exception messages, which can save hours of troubleshooting time.
In this tutorial, I’ll show you multiple methods to throw exceptions with custom messages in PowerShell, along with practical examples.
What is an Exception in PowerShell?
An exception in PowerShell is how the system communicates that something unexpected has occurred during script execution. Think of it as PowerShell’s way of saying, “I’ve encountered a problem I can’t handle on my own.”
Exceptions provide useful information about what went wrong, where it happened, and sometimes even how to fix it.
When writing any PowerShell scripts, especially in the production environment, knowing how to throw and handle exceptions properly is essential to:
- Prevent scripts from continuing with invalid data
- Provide meaningful error messages
- Enable proper logging of issues
- Allow controlled script termination
Check out How to Log Error Messages to a File Using PowerShell Try Catch?
PowerShell provides different methods to throw exceptions with messages. Let me show you those methods with examples.
Method 1: Using the throw Statement
The simplest way to throw an exception in PowerShell is by using the throw statement.
function Test-Connection {
param (
[string]$ServerName
)
if ([string]::IsNullOrEmpty($ServerName)) {
throw "Server name cannot be empty. Please provide a valid server name."
}
# Rest of the function
}When you execute this function without providing a server name, it will throw an exception with your custom message.
Here’s what happens when you call the function without a parameter:
PS> Test-Connection
Exception: Server name cannot be empty. Please provide a valid server name.
At line:6 char:9
+ throw "Server name cannot be empty. Please provide a valid server name ..."
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Server name cannot be empty.:String) [], RuntimeException
+ FullyQualifiedErrorId : Server name cannot be empty.What I love about this method is its simplicity. It’s direct, easy to read, and works well for most basic scenarios.
Here is another simple example of using the throw keyword in PowerShell.
function Divide-Numbers {
param (
[int]$Numerator,
[int]$Denominator
)
if ($Denominator -eq 0) {
throw "Divide by zero error: Denominator cannot be zero."
}
$result = $Numerator / $Denominator
Write-Output "Result: $result"
}
Divide-Numbers -Numerator 10 -Denominator 0You can see the output like in the screenshot below.

Check out Create a Log File with Date and Time in PowerShell
Method 2: Using throw with Error Record
For more complex scenarios, you can throw an exception with an ErrorRecord, which allows you to specify more detailed information.
Here is an example and the complete PowerShell script.
function Connect-Database {
param (
[string]$ConnectionString
)
if ([string]::IsNullOrEmpty($ConnectionString)) {
$errorId = 'ConnectionStringMissing'
$errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
$errorMessage = "Connection string cannot be empty. Format should be: 'Server=myServerName;Database=myDatabase;'"
$exception = New-Object System.ArgumentException $errorMessage
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null
throw $errorRecord
}
# Database connection logic here
}This method gives you more control over the exception details, including:
- Error ID
- Error category
- Custom message
- Exception type
This approach is particularly useful when you need to differentiate between various error types or when you want to provide richer error information.
Read Read Log Files with PowerShell
Method 3: Using Write-Error with -ErrorAction Stop
Another approach is to use Write-Error with the -ErrorAction Stop parameter:
function Process-Customer {
param (
[string]$CustomerId
)
if (-not ($CustomerId -match '^\d{6}$')) {
Write-Error -Message "Invalid customer ID format: $CustomerId. The customer ID must be a 6-digit number." -ErrorAction Stop
}
Write-Output "Customer ID $CustomerId is valid."
}When you run this with an invalid customer ID:
PS> Process-Customer -CustomerId "ABC123"
Process-Customer: Invalid customer ID format: ABC123. The customer ID must be a 6-digit number.I’ve found this method useful in scripts where I want to leverage PowerShell’s built-in error handling system while still ensuring execution stops.
You can see the exact error message like in the screenshot below:

Check out Create a Log File in PowerShell
Method 4: Throwing Specific Exception Types
You can also throw specific messages for specific exception types in PowerShell.
Here is an example and the complete PowerShell script.
function Get-UserData {
param (
[string]$UserId,
[string]$FilePath
)
if ([string]::IsNullOrEmpty($UserId)) {
throw [System.ArgumentNullException]::new("UserId", "User ID cannot be null or empty")
}
if (-not (Test-Path $FilePath)) {
throw [System.IO.FileNotFoundException]::new("Could not find the user data file: $FilePath", $FilePath)
}
# Process user data
}This approach is particularly effective when:
- You need to differentiate between various error conditions
- Your script interfaces with .NET code that expects specific exception types
- You want to provide very detailed error information
Read Set Password for Local User in Windows 11 Using PowerShell
Method 5: Using Try-Catch with Custom Exception Throwing
When you want to handle exceptions and potentially re-throw them with additional context:
function Update-EmployeeRecord {
param (
[string]$EmployeeId,
[hashtable]$NewData
)
try {
$employee = Get-Employee -Id $EmployeeId
# Update logic here
# If something goes wrong during the update
if (-not $UpdateSuccessful) {
throw "Failed to update employee record for ID: $EmployeeId"
}
}
catch [System.Net.WebException] {
throw "Network error while updating employee $EmployeeId. Please check your connection: $($_.Exception.Message)"
}
catch {
# Re-throw with additional context
throw "Error updating employee $EmployeeId: $($_.Exception.Message)"
}
}I’ve used this pattern extensively in production scripts because it allows me to:
- Catch specific types of exceptions separately
- Add contextual information to the error message
- Create a cleaner error handling flow
Check out Create Files with Content Using PowerShell
Best Practices for Throwing Exceptions in PowerShell
Based on my experience, here are some best practices to follow:
1. Be Specific with Error Messages
Always include:
- What went wrong
- Why it went wrong
- What the expected input/state should be
Here is an example.
- BAD:
throw "Invalid input." - GOOD:
throw "Customer ID '12345' is invalid. It must be a 6-digit number starting with 9."
2. Use Appropriate Exception Types
Match your exception type to the error condition:
| Error Condition | Recommended Exception Type |
|---|---|
| Invalid parameter | System.ArgumentException |
| Missing parameter | System.ArgumentNullException |
| File not found | System.IO.FileNotFoundException |
| Permission issues | System.UnauthorizedAccessException |
| General errors | System.Management.Automation.RuntimeException |
3. Include Relevant Data
Always include the value that caused the problem:
if ($age -lt 18) {
throw "Age $age is below the minimum required age of 18 years."
}4. Document Your Error Handling Approach
For team projects, document how errors are thrown and should be handled:
<#
.SYNOPSIS
Processes customer orders.
.DESCRIPTION
Processes customer orders from the system.
.NOTES
Error Handling:
- Throws ArgumentException for invalid order IDs
- Throws UnauthorizedAccessException for permission issues
- Throws FileNotFoundException if order file is missing
#>
function Process-CustomerOrder {
# Function implementation
}Read Create a File in the Current Directory Using PowerShell
Real-World Example: File Processing Script
Here’s a real example showing exception throwing in a real-world scenario:
function Process-SalesReports {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$ReportFolder,
[Parameter()]
[string]$OutputFolder = "C:\Processed",
[Parameter()]
[string]$Region = "West"
)
# Validate folders
if (-not (Test-Path $ReportFolder)) {
throw [System.IO.DirectoryNotFoundException]::new("Report folder does not exist: $ReportFolder")
}
if (-not (Test-Path $OutputFolder)) {
try {
New-Item -Path $OutputFolder -ItemType Directory -Force
}
catch {
throw "Failed to create output folder $OutputFolder : $($_.Exception.Message)"
}
}
# Validate region
$validRegions = @("East", "West", "North", "South")
if ($validRegions -notcontains $Region) {
throw [System.ArgumentException]::new("Invalid region specified: '$Region'. Valid regions are: $($validRegions -join ', ')")
}
# Get reports
$reports = Get-ChildItem -Path $ReportFolder -Filter "Sales_*.csv"
if ($reports.Count -eq 0) {
throw "No sales reports found in $ReportFolder. Expected files matching pattern 'Sales_*.csv'"
}
# Process each report
foreach ($report in $reports) {
try {
$data = Import-Csv -Path $report.FullName
# Filter for region
$regionData = $data | Where-Object { $_.Region -eq $Region }
if ($regionData.Count -eq 0) {
Write-Warning "No data for region '$Region' found in report: $($report.Name)"
continue
}
# Process and save
$outputFile = Join-Path -Path $OutputFolder -ChildPath "Processed_$($report.Name)"
$regionData | Export-Csv -Path $outputFile -NoTypeInformation
Write-Verbose "Successfully processed $($report.Name) for region $Region"
}
catch [System.IO.IOException] {
throw "I/O error processing report $($report.Name): $($_.Exception.Message)"
}
catch {
throw "Error processing report $($report.Name): $($_.Exception.Message)"
}
}
Write-Output "Successfully processed $($reports.Count) sales reports for region $Region"
}This script demonstrates:
- Multiple exception types based on different error conditions
- Contextual error messages that include the problematic values
- A hierarchical approach to error handling
Check out Create JSON Files with Content Using PowerShell
Integrate Exception Handling with Logging in PowerShell
In production environments, I always pair exception throwing with proper logging:
function Add-Customer {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter(Mandatory=$true)]
[string]$Email
)
try {
# Validate email format
if ($Email -notmatch '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') {
Write-Error "Invalid email format: $Email" -ErrorAction Stop
}
# Add customer logic
# ...
Write-Verbose "Successfully added customer: $Name"
return $true
}
catch {
# Log the error
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] ERROR: Failed to add customer. $($_.Exception.Message)"
Add-Content -Path "C:\Logs\customer_operations.log" -Value $logMessage
# Re-throw for the caller to handle
throw
}
}I’ve found that this combination of throwing exceptions and logging provides the best of both worlds: immediate error notification and a permanent record for later
Check out PowerShell to Get the Current Logged On User on a Remote Computer
Handling Exceptions vs. Throwing Exceptions
Now, let me explain the differences between handling exceptions and throwing exceptions in PowerShell. Here is an example to help you understand this topic.
function Update-CustomerRecord {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$CustomerId,
[Parameter(Mandatory=$true)]
[hashtable]$UpdateData
)
try {
# Validate input
if ($CustomerId -notmatch '^\d{6}$') {
throw [System.ArgumentException]::new("Customer ID must be a 6-digit number, got: $CustomerId", "CustomerId")
}
# Try to perform update
$result = Invoke-DatabaseUpdate -CustomerId $CustomerId -Data $UpdateData
return $result
}
catch [System.ArgumentException] {
# We created this exception, so just re-throw it
throw
}
catch [System.Data.SqlClient.SqlException] {
# Database error - add context and re-throw
throw "Database error while updating customer $CustomerId. Error: $($_.Exception.Message)"
}
catch {
# Unexpected error - log it and throw a user-friendly message
Write-Log -Level Error -Message "Unexpected error: $($_.Exception.Message)"
throw "An unexpected error occurred while updating customer $CustomerId. Please contact support."
}
}I follow this rule of thumb: throw exceptions for conditions that shouldn’t occur in normal operation, and handle exceptions for conditions that might be expected (like network issues or file permissions).
Check out Get Windows Activation Status Using PowerShell
Exception Handling Across Multiple Functions in PowerShell
When building complex scripts, I often implement a cascading exception handling pattern:
function Get-Customer {
[CmdletBinding()]
param([string]$Id)
if ([string]::IsNullOrEmpty($Id)) {
throw [System.ArgumentNullException]::new("Id", "Customer ID cannot be null or empty")
}
# Get customer logic
}
function Update-Customer {
[CmdletBinding()]
param(
[string]$Id,
[hashtable]$Properties
)
try {
$customer = Get-Customer -Id $Id
# Update customer
}
catch [System.ArgumentNullException] {
# Simply re-throw as this is an input validation error
throw
}
catch {
throw "Failed to update customer $Id. Error: $($_.Exception.Message)"
}
}
function Sync-CustomerDatabase {
[CmdletBinding()]
param()
try {
$customers = Import-Csv "C:\Data\customers.csv"
foreach ($customer in $customers) {
try {
Update-Customer -Id $customer.Id -Properties @{
Name = $customer.Name
Email = $customer.Email
}
Write-Verbose "Updated customer $($customer.Id)"
}
catch {
# Log error but continue with other customers
Write-Warning "Could not update customer $($customer.Id): $($_.Exception.Message)"
}
}
}
catch {
# Fatal error for the entire operation
throw "Customer database sync failed: $($_.Exception.Message)"
}
}This pattern allows different levels of error handling:
- At the lowest level, we throw specific exceptions
- At mid-levels, we re-throw with added context
- At the top level, we handle non-fatal errors and throw only for fatal ones
Read PowerShell Write-Host vs Write-Error
Using Custom Exception Classes
This example is for a little advanced PowerShell user. For larger PowerShell modules, I sometimes create custom exception classes:
# Define custom exception class
class CustomerServiceException : System.Exception {
[string]$CustomerId
[string]$Operation
CustomerServiceException([string]$message, [string]$customerId, [string]$operation) : base($message) {
$this.CustomerId = $customerId
$this.Operation = $operation
}
}
function Update-CustomerStatus {
[CmdletBinding()]
param(
[string]$CustomerId,
[string]$NewStatus
)
$validStatuses = @("Active", "Inactive", "Pending")
if ($validStatuses -notcontains $NewStatus) {
throw [CustomerServiceException]::new(
"Invalid status '$NewStatus'. Valid values are: $($validStatuses -join ', ')",
$CustomerId,
"StatusUpdate"
)
}
# Update logic here
}This provides extremely rich error information that can be caught and handled specifically:
try {
Update-CustomerStatus -CustomerId "123456" -NewStatus "Deleted"
}
catch [CustomerServiceException] {
Write-Host "Customer Service Error:"
Write-Host " Customer ID: $($_.Exception.CustomerId)"
Write-Host " Operation: $($_.Exception.Operation)"
Write-Host " Message: $($_.Exception.Message)"
}Conclusion
As a PowerShell developer, you should know how to throw exceptions with informative messages while writing any PowerShell scripts. When I first started with PowerShell, I often neglected error handling, but I quickly learned that exception handling is what separates production-ready scripts from quick one-off utilities.
I hope you now have a complete idea of throwing exceptions with messages in PowerShell with the above examples and the complete PowerShell scripts.
I recommend reviewing your existing scripts and updating their error handling based on these techniques.
You may also like:
Bijay Kumar is an esteemed author and the mind behind PowerShellFAQs.com, where he shares his extensive knowledge and expertise in PowerShell, with a particular focus on SharePoint projects. Recognized for his contributions to the tech community, Bijay has been honored with the prestigious Microsoft MVP award. With over 15 years of experience in the software industry, he has a rich professional background, having worked with industry giants such as HP and TCS. His insights and guidance have made him a respected figure in the world of software development and administration. Read more.