I\'m trying to get my script to stop if it hits an error, and use try... catch to give me an easy way to handle the error. The easiest thing in the world I\'d have thought,
Try-Catch will catch an exception and allow you to handle it, and perhaps handling it means to stop execution... but it won't do that implicitly. It will actually consume the exception, unless you rethrow it. But your issue is simpler than that -- the try block takes precedence over the -ErrorAction stop
in your get-content cmdlet. So instead of stopping execution, you get taken to the Catch
block and continue on because there is no error handling in the catch block.
Try removing the try-catch logic from your script, and allow the cmdlet to error out:
get-content "c:\GarbageFileName.txt" -ErrorAction stop
write-output "You won't reach me if GarbageFileName doesn't exist."
And you should get the desired result of execution not reaching write-output
:
PS C:\> .\so-test.ps1
Get-Content : Cannot find path 'C:\GarbageFileName.txt' because it does not exist.
At C:\so-test.ps1:2 char:12
+ get-content <<<< "c:\GarbageFileName.txt" -ErrorAction stop
+ CategoryInfo : ObjectNotFound: (C:\GarbageFileName.txt:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
The whole idea of a try/catch control is that you tell the script what to do if it encounters a terminating error, instead of the default action of throwing the error and stopping the script. If your catch block just displays a message to the terminal with Write-Host, that's all the error handling there will be, and the script will continue from there. If you think about it, it would partially defeat the purpose of try/catch if the script were stopped automatically whenever an error is caught.
In the catch block, $_ will be set to the ErrorRecord object representing the terminating error from the try block (the same one that gets stored in $error[0]). So the simplest way to end the script is to rethrow the error that would have been thrown if you hadn't used a try/catch:
try {
Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
# Custom action to perform before terminating
throw $_
}
Or, if you want to display a custom message instead of the default ErrorRecord:
try {
Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
throw 'Custom error message'
}
Or you could use break as suggested in Joost's answer if you want to just quit after you're finished with your custom error handling without throwing an error to the error stream.
Or you could get more sophisticated and create your own ErrorRecord object. There's a lot you can do with that, it's too big a topic to cover comprehensively here, but you can get more info about the syntax by googling System.Management.Automation.ErrorRecord. Here's an example from one of my scripts to get you started (from a function that executes a SQL query defined in the $query variable against a SQL Server database):
} catch {
$ErrorRecord = New-Object System.Management.Automation.ErrorRecord(
(New-Object Exception("Exception executing the query: $($_.Exception.InnerException.Message)")),
$query,
[System.Management.Automation.ErrorCategory]::InvalidArgument,
$null
)
$ErrorRecord.CategoryInfo.Reason = $_.CategoryInfo.Reason;
$ErrorRecord.CategoryInfo.Activity = $_.InvocationInfo.InvocationName;
$PSCmdlet.ThrowTerminatingError($ErrorRecord);
}
A couple of notes:
[CmdletBinding()]
at the beginning of the function or script. Otherwise, you can just use throw $ErrorRecord
to throw your custom error. However, the result will be more Cmdlet-style if you use $PSCmdlet.ThrowTerminatingError. (throw will spit back the line from the function that generated the error, whereas $PSCmdlet.ThrowTerminatingError will give you the line from the calling context where the function was used. It's hard to describe in a way that makes sense without getting too elaborate, but if you experiment with it you'll see what I mean.)BTW, it's redundant to set $ErrorActionPreference = "Stop"
, and then use -ErrorAction Stop
. The preference variable sets the default action for all cmdlets, and the -ErrorAction switch overrides the default action for a particular cmdlet, so there's no need to first specify the default, then use -ErrorAction to specify the same action you just set as the default. What you probably want to do is just leave out $ErrorActionPreference = "Stop"
.
The only thing missing is your break
-statement in the Catch-block. Powershell won't stop the script if you don't instruct it to.
try {
get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
write-output "in catch, I want it to stop now"
break
}
write-output "try-catch finished, script is continuing"
And a small addendum, in case it helps you: with finally
, you can add some lines of code that are always executed, regardless of wether an exception was thrown or not.
try {
get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
write-output "in catch, I want it to stop now"
break
}
finally {
#do some stuff here that is executed even after the break-statement, for example:
Set-Content -Path "f:\GarbageFileName.txt" -Value $null
}
#the line below is executed only if the error didn't happen
write-output "try-catch finished, script is continuing"