InteXX InteXX - 3 months ago 10
PowerShell Question

How to get PowerShell error detail from function called from inside a ForEach loop

This code works as expected:

function Foo ($Dividend) {
$Divisor = 0
$Quotient = $Dividend / $Divisor
}

try {
Foo 1
} catch {
$Line = $($_.InvocationInfo.Line).Trim()
$Row = $($_.InvocationInfo.ScriptLineNumber)
$Col = $($_.InvocationInfo.OffsetInLine)

Write-Host "Error: $_" -ForegroundColor "Red"
Write-Host " -> Row: $Row" -ForegroundColor "Red"
Write-Host " -> Col: $Col" -ForegroundColor "Red"
Write-Host " -> Line: $Line" -ForegroundColor "Red"
}


It prints this text:

Error: Attempted to divide by zero.
-> Row: 3
-> Col: 5
-> Line: $Quotient = $Dividend / $Divisor


However this fails:

function Foo ($Dividend) {
$Divisor = 0
$Quotient = $Dividend / $Divisor
}

try {
$Dividends = @(1)

$Dividends | ForEach {
Foo $_
}
} catch {
$Line = $($_.InvocationInfo.Line).Trim()
$Row = $($_.InvocationInfo.ScriptLineNumber)
$Col = $($_.InvocationInfo.OffsetInLine)

Write-Host "Error: $_" -ForegroundColor "Red"
Write-Host " -> Row: $Row" -ForegroundColor "Red"
Write-Host " -> Col: $Col" -ForegroundColor "Red"
Write-Host " -> Line: $Line" -ForegroundColor "Red"
}


It prints this:

Error: Attempted to divide by zero.
-> Row: 11
-> Col: 22
-> Line: $Dividends | ForEach {


Is there a way to get properly scoped error information when a function is called from within a
ForEach
loop?

Answer

The information you're looking for is nested inside the top-level ErrorRecord object. You need to unroll the error object to get to the nested information.

Change this:

} catch {
    $Line = $($_.InvocationInfo.Line).Trim()
    $Row = $($_.InvocationInfo.ScriptLineNumber)
    $Col = $($_.InvocationInfo.OffsetInLine)

    Write-Host "Error: $_" -ForegroundColor "Red"
    Write-Host "  -> Row:    $Row" -ForegroundColor "Red"
    Write-Host "  -> Col:    $Col" -ForegroundColor "Red"
    Write-Host "  -> Line:   $Line" -ForegroundColor "Red"
}

into this:

} catch {
    $Line = $($_.Exception.ErrorRecord.InvocationInfo.Line).Trim()
    $Row = $($_.Exception.ErrorRecord.InvocationInfo.ScriptLineNumber)
    $Col = $($_.Exception.ErrorRecord.InvocationInfo.OffsetInLine)

    Write-Host "Error: $_" -ForegroundColor "Red"
    Write-Host "  -> Row:    $Row" -ForegroundColor "Red"
    Write-Host "  -> Col:    $Col" -ForegroundColor "Red"
    Write-Host "  -> Line:   $Line" -ForegroundColor "Red"
}

and the code should behave as you expect.