49

I recently wrote a PowerShell script that works great - however, I'd like to now upgrade the script and add some error checking / handling - but I've been stumped at the first hurdle it seems. Why won't the following code work?

try {
  Remove-Item "C:\somenonexistentfolder\file.txt" -ErrorAction Stop
}

catch [System.Management.Automation.ItemNotFoundException] {
  "item not found"
}

catch {
  "any other undefined errors"
  $error[0]
}

finally {
  "Finished"
}

The error is caught in the second catch block - you can see the output from $error[0]. Obviously I would like to catch it in the first block. What am I missing?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
steve
  • 849
  • 2
  • 9
  • 15

2 Answers2

41

-ErrorAction Stop is changing things for you. Try adding this and see what you get:

Catch [System.Management.Automation.ActionPreferenceStopException] {
"caught a StopExecution Exception" 
$error[0]
}
jpaugh
  • 6,634
  • 4
  • 38
  • 90
Bruce
  • 1,633
  • 1
  • 11
  • 12
  • That's pretty interesting, considering `$_.Exception` is an `ItemNotFoundException` and not an `ActionPreferencesStopException`. I bet the latter drives from `RuntimeException`. – Joel B Fant Jul 21 '11 at 17:34
  • I think you're right. I like your switch version. More in line with what steve is trying to do. – Bruce Jul 21 '11 at 17:49
  • 8
    Right, the trouble here is that it is a non-terminating error. So even though it is a `ItemNotFoundException`, it isn't actually getting thrown unless wrapped in a `ActionPreferencesStopException`. As a developer, this annoys me. :) – JasonMArcher Jul 21 '11 at 23:32
  • 2
    @JasonMArcher Seems like a bug in an older version. This is working for me as expected in Powershell 5.0 That is it catches the `ItemNotFoundException` – Jason S Jan 22 '19 at 20:00
  • @JasonS Yay, it looks like you no longer have to worry about the exception getting wrapped. – JasonMArcher Jan 25 '19 at 22:54
31

That is very odd.

I went through ItemNotFoundException's base classes and tested the following multiple catches to see what would catch it:

try {
  remove-item C:\nonexistent\file.txt -erroraction stop
}
catch [System.Management.Automation.ItemNotFoundException] {
  write-host 'ItemNotFound'
}
catch [System.Management.Automation.SessionStateException] {
  write-host 'SessionState'
}
catch [System.Management.Automation.RuntimeException] {
  write-host 'RuntimeException'
}
catch [System.SystemException] {
  write-host 'SystemException'
}
catch [System.Exception] {
  write-host 'Exception'
}
catch {
  write-host 'well, darn'
}

As it turns out, the output was 'RuntimeException'. I also tried it with a different exception CommandNotFoundException:

try {
  do-nonexistent-command
}
catch [System.Management.Automation.CommandNotFoundException] {
  write-host 'CommandNotFoundException'
}
catch {
  write-host 'well, darn'
}

That output 'CommandNotFoundException' correctly.

I vaguely remember reading elsewhere (though I couldn't find it again) of problems with this. In such cases where exception filtering didn't work correctly, they would catch the closest Type they could and then use a switch. The following just catches Exception instead of RuntimeException, but is the switch equivalent of my first example that checks all base types of ItemNotFoundException:

try {
  Remove-Item C:\nonexistent\file.txt -ErrorAction Stop
}
catch [System.Exception] {
  switch($_.Exception.GetType().FullName) {
    'System.Management.Automation.ItemNotFoundException' {
      write-host 'ItemNotFound'
    }
    'System.Management.Automation.SessionStateException' {
      write-host 'SessionState'
    }
    'System.Management.Automation.RuntimeException' {
      write-host 'RuntimeException'
    }
    'System.SystemException' {
      write-host 'SystemException'
    }
    'System.Exception' {
      write-host 'Exception'
    }
    default {'well, darn'}
  }
}

This writes 'ItemNotFound', as it should.

Joel B Fant
  • 24,406
  • 4
  • 66
  • 67