I have this catch block in my Powershell script.
catch [System.Data.SqlClient.SqlException]
{
Write-Host "$_"
Exit 2
}
I would really like to be able to switch on the error number.
I know atleast in C# there's a property on the SqlException called number. Isn't that also true for Powershell?
If the property is there, how do I access it?
Thank you very much in advance
You should be able to access it in your catch block using:
$_.Exception.Number
i.e.
catch [System.Data.SqlClient.SqlException]
{
Write-Host $_.Exception.Number
Exit 2
}
Related
Below code keep executing when error arises,
foreach($url in Get-Content $urlsDir)
{
try
{
// do something
// declare X
}
catch
{
// write host or soemthing with exception
continue
}
finally
{
// dispose X
}
}
but when I put this code in RunWithElevatedPrivileges, it completely stops on first error and won't continue execution,
[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges({
# Iterate through all webs in a text file
foreach($url in Get-Content $urlsDir)
{
try
{
// do something
// declare X
}
catch
{
// write host or soemthing with exception
continue
}
finally
{
// dispose X
}
}
});
It could be related to whether or not you've specified an ErrorAction, I'm not sure how this relates to try catch though. I have done something similar where my foreach would not stop unless explicitly stated.
Basically you need to specify what should happen if an error occurs on each call, as so
Call-Something $SomeParam -ErrorAction Stop
and unless you specify it as above for each call or in the start of your script as below errors can be silently ignored.
// at the start of your script
$ErrorActionPreference = "Stop"
For more information you can read about ErrorAction in powershell here for instance
https://blogs.msdn.microsoft.com/kebab/2013/06/09/an-introduction-to-error-handling-in-powershell/
And according to msdn these are the valid values:
Stop: Displays the debug message and stops executing. Writes an error to the console.
Inquire: Displays the debug message and asks you whether you want to continue. Note that adding the Debug common parameter to a command--when the command is configured to generate a debugging message--changes the value of the $DebugPreference variable to Inquire.
Continue: Displays the debug message and continues with execution.
SilentlyContinue: No effect. The debug message is not (Default) displayed and execution continues without interruption.
I'm wondering why I get the following behaviour when running this script. I have the script loaded in PowerShell ISE (v4 host) and have the Pester module loaded. I run the script by pressing F5.
function Test-Pester {
throw("An error")
}
Describe "what happens when a function throws an error" {
Context "we test with Should Throw" {
It "Throws an error" {
{ Test-Pester } | Should Throw
}
}
Context "we test using a try-catch construct" {
$ErrorSeen = $false
try {
Test-Pester
}
catch {
$ErrorSeen = $true
}
It "is handled by try-catch" {
$ErrorSeen | Should Be $true
}
}
Context "we test using trap" {
trap {
$ErrorSeen = $true
}
$ErrorSeen = $false
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
I then get the following output:
Describing what happens when a function throws an error
Context we test with Should Throw
[+] Throws an error 536ms
Context we test using a try-catch construct
[+] is handled by try-catch 246ms
Context we test using trap
An error
At C:\Test-Pester.ps1:2 char:7
+ throw("An error")
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (An error:String) [], RuntimeException
+ FullyQualifiedErrorId : An error
[-] is handled by trap 702ms
Expected: {True}
But was: {False}
at line: 40 in C:\Test-Pester.ps1
40: $ErrorSeen | Should Be $true
Question
Why is the trap{} apparently not running in the final test?
Here are two solutions to the problem based on the comments/suggested answers from #PetSerAl and #Eris. I have also read and appreciated the answer given to this question:
Why are variable assignments within a Trap block not visible outside it?
Solution 1
Although variables set in the script can be read within the trap, whatever you do within the trap happens to a copy of that variable, i.e. only in the scope that is local to the trap. In this solution we evaluate a reference to $ErrorSeen so that when we set the value of the variable, we are actually setting the value of the variable that exists in the parent scope.
Adding a continue to the trap suppresses the ErrorRecord details, cleaning up the output of the test.
Describe "what happens when a function throws an error" {
Context "we test using trap" {
$ErrorSeen = $false
trap {
Write-Warning "Error trapped"
([Ref]$ErrorSeen).Value = $true
continue
}
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
Solution 2
Along the same lines as the first solution, the problem can be solved by explicitly setting the scope of the $ErrorSeen variable (1) when it is first created and (2) when used within the trap {}. I have used the Script scope here, but Global also seems to work.
Same principle applies here: we need to avoid the problem where changes to the variable within the trap only happen to a local copy of the variable.
Describe "what happens when a function throws an error" {
Context "we test using trap" {
$Script:ErrorSeen = $false
trap {
Write-Warning "Error trapped"
$Script:ErrorSeen = $true
continue
}
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
According to this blog, you need to tell your trap to do something to the control flow:
The [...] thing you notice is that when you run this as script, you will receive both your error message and the red PowerShell error message.
. 'C:\Scripts\test.ps1'
Something terrible happened!
Attempted to divide by zero.
At C:\Scripts\test.ps1:2 Char:3
+ 1/ <<<< null
This is because your Trap did not really handle the exception. To handle an exception, you need to add the "Continue" statement to your trap:
trap { 'Something terrible happened!'; continue }
1/$null
Now, the trap works as expected. It does whatever you specified in the trap script block, and PowerShell does not get to see the exception anymore. You no longer get the red error message.
I have a 1500+ lines Powershell script that has been enhanced to log progress and errors to Azure Table Storage.
The problem is that not all errors are logged.
Is this the easiest way to accomplish this without changing the script too much and catching all errors?
$ErrorActionPreference = "Stop"
function a
{
"a"
}
function b
{
"b"
Get-Content foo
}
function c
{
"c"
}
Try
{
a
b
c
}
Catch
{
"Catch & Log Error"
$Error[0].Exception
}
Finally
{
"The End"
}
UPDATE
The output is:
a
b
Catch & Log Error
Cannot find path 'C:\foo' because it does not exist.
The End
You can place this at the top of your script:
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
... all errors will then 'act' like terminating errors & be caught by a try...catch block.
I am making several calls to .NET classes in order in my PS script like this:
[class1]::MethodA()
[class1]::MethodB()
if(/*check if last method threw an error*/)
{
"MethodB failed! Exiting."
return
}
[class2]::MethodC()
[class2]::MethodD()
I really want to check whether there was an exception from MethodB before moving on. How do check for this?
Use a try/catch block:
try
{
[class1]::MethodB()
}
catch
{
Write-Host "MethodB failed!"
exit
}
I have a try-catch statement within a try-catch statement. The inner catch catches the error, but the throw does not cause the error to be caught in the out catch statement. Breifly, my script is formatted similar to:
$ErrorPreference = "Stop"
try
{
getStuffFromDB
putStuffInDB
}
catch
{
write-host ("Error: " + $error[0])
}
function getStuffFromDB
{
try
{
-- database query statement
}
catch
{
throw
}
finally
{
close connection and clean up
}
}
function putStuffInDB
{
try
{
-- database insert statements statement
}
catch
{
throw
}
finally
{
close connection and clean up
}
}
When I ran the script there were no errors, but I noticed the SQL Server database that I was attempting to populate was missing data. When I re-ran the script in debug, the function 'putStuffInDB' had an error that was caught in the catch block. But when I stepped the message did not get 'thrown' to the outer catch block, but processed the finally block and terminated.
I am obviously missing something that I am not seeing. I have used the construct in C# in the past and never had issues with errors being 'passed' to the outer catch block.
I am not seeing that behavior. I ran the following in PowerShell ISE and it produces the expected results. Is it possible that the errors in the database were not in fact thrown as exceptions? I believe in SQL Server for example, certain errors under a given error level are not thrown as exceptions back to the ADO.NET provider.
$ErrorActionPreference = 'Stop'
function Throw1 {
try {
Write-Host "Throw1.Try"
throw "Error from Throw1"
}
catch {
Write-Host "Throw1.Catch"
throw
}
finally {
Write-Host "Throw1.Finally"
}
}
function Throw2 {
try {
Write-Host "Throw2.Try"
throw "Error from Throw2"
}
catch {
Write-Host "Throw2.Catch"
throw
}
finally {
Write-Host "Throw2.Finally"
}
}
function Test {
try {
Throw1
Throw2
}
catch {
Write-Host $error[0]
}
}
Test
Produces the following:
Throw1.Try
Throw1.Catch
Throw1.Finally
Error from Throw1
The variable you want to set is $ErrorActionPreference, not $ErrorPreference.
(Josh did set the right variable.)
I realized that the problem was of my own doing. In the POSH functions to create the SQLServer entries I returned the primary key of the data set created. The design of the functions was such that the function would return the primary key. The design mistake was that I put a return statement in the finally block which superceded the throw back to the outer catch. I have changed the design removing the return statement. The try/catch now works correctly.