4

I'm trying to catch Exception calling a function running another function like this:

$ErrorActionPreference = "Stop"
function f {
    $a = 1
    $b = $a / 0
}
function Main($f) {
    try {
        $f
    } catch [System.Exception] {
        "Caught exception"
    }
}
Main(f)

The problem is that the Exception is not caught and powershell show message like this:

Attempted to divide by zero.
In C:\test.ps1:4 car:5
+     $b = $a / 0
+     ~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException

Why the exception is not caught even if $ErrorActionPreference = "Stop" is on the top of code?

guidros
  • 41
  • 3

2 Answers2

3

The problem is really at this line Main(f). What that does is call the function f and attempt to pass the result to the function Main. Of course before it can get the result there is an exception and the pipeline never gets into Main. You were trying to pass the function itself not the result. PowerShell doesn't have great ways to do this directly. It's a scripting language not a functional language. However you can declare the parameter to Main as a ScriptBlock and invoke it. A ScriptBlock is something that operates largely like a function object. Using it will have largely the same effect. However it's somewhat cumbersome to get a function's ScriptBlock. This should work as expected and is more standard style:

$ErrorActionPreference = "Stop"
function f {
    $a = 1
    $b = $a / 0
}

function Main {    
    param([ScriptBlock]$f)

    try {
        & $f
    } catch [System.Exception] {
        "Caught exception"
    }
}

Main -f { f }

So instead we are passing a new scriptblock that just contains a call to f and invoking that wrapper.

Note that I've replaced the C-style function call syntax. In my experience that style is more trouble than its worth in PowerShell. Stick to the PowerShell style and things are often clearer.

If you really want to get the function's ScriptBlock, you need to do something like:

Main -f (get-command f).ScriptBlock
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
2

Well, you are not calling your Main function, you are calling the f function and passing the output of that as the input of the Main and then calling Main. Use Set-PSDebug and see.

Set-PSDebug -Trace 2

And testing now:

Main(f)

DEBUG:     ! CALL function '<ScriptBlock>'
DEBUG:     ! SET $ErrorActionPreference = 'Stop'.
DEBUG:   15+  >>>> Main(f)
DEBUG:    2+ function f  >>>> {

DEBUG:     ! CALL function 'f'
DEBUG:    3+  >>>> 1/0

While this returns 1.

function f {
1/1
}
function Main($f) {
    try {
        $f
    } catch [System.Exception] {
        "Caught exception"
    }
}

Main(f)

And this catches as expected:

function f {
1/1
}
function Main($f) {
    try {
        $f/0
    } catch [System.Exception] {
        "Caught exception"
    }
}

Main(f)

edit: To clarify the wrong second answer, this has nothing to do with precompilation, and its easily verifiable with the following code:

function f {
$a = 0
1/$a
}
function Main($input) {
    try {
        $input
    } catch [System.Exception] {
        "Caught exception"
    }
}

Main(f)
4c74356b41
  • 69,186
  • 6
  • 100
  • 141