Is there a way to enter the debugger on an error? Is there a way to enter the debugger on an error? powershell powershell

Is there a way to enter the debugger on an error?


Of course. You can create conditional breakpoints in PowerShell, using the Set-PSBreakpoint cmdlet. Consider the following code. Save it as a script file, and execute it. There are in-line comments to help you understand what's going on.

Keep in mind that there are three different types of breakpoints:

  • Line
  • Variable
  • Command

Command Breakpoint

This code example is using the command breakpoint type, because I am telling it to only set breakpoints on Get-WmiObject commands. You can alternately specify a specific line number or a variable breakpoint type. You use the -Action parameter to specify the conditions under which you want the breakpoint to be set. You must use the break keyword somewhere inside the -Action ScriptBlock in order to instruct the debugger to pause execution of the PowerShell script.

# 1. Reset $Error to $null$WmiError = $null;# 2. Clean up any existing breakpointsGet-PSBreakpoint | Remove-PSBreakpoint;# 3. Set breakpoint, but only on Get-WmiObject commands, when the $WmiError variable is not $nullSet-PSBreakpoint -Command Get-WmiObject -Action { if ($WmiError) { break; } };# 4. Failed Get-WmiObject commandGet-WmiObject -Class Win32_NonExistentClass -ErrorVariable WmiError;# 5. Successful Get-WmiObject command#    PowerShell breaks here, because:#     - It's a Get-WmiObject command#     - The $WmiError variable is not nullGet-WmiObject -Class Win32_BIOS;

Since you mentioned using Write-Error, you could set a PSBreakpoint on lines where Write-Error appears. Here is an example of how to do that:

Set-PSBreakpoint -Command Write-Error -Action { break; };

Pretty easy, right?

Variable Breakpoint

This example uses the variable PSBreakpoint type, but only when the variable's contents are modified. You can use the -Mode parameter to determine under what conditions the variable breakpoint is hit:

  • Read
  • ReadWrite
  • Write

Code:

# 1. Clean up any existing breakpointsGet-PSBreakpoint | Remove-PSBreakpoint;# 2. Set a PSBreakpoint of type "variable" on a variable named "Data," but only when it has changedSet-PSBreakpoint -Action { Write-Host -ForegroundColor Green -Object ('The $Data variable has changed! Value is: {0}' -f $Data); break; } -Variable Data -Mode Write;# 3. No break on this line, because we are not changing the variableWrite-Host -Object $Data;# 4. Execution is paused on this line, because we change the variable$Data = 1;

Line Breakpoint

Now that we've looked at the variable and command PSBreakpoint types, the last type of breakpoint to explore is the line breakpoint. If you were to copy/paste the code below, save it, and execute it, you would see that the code breaks on the Write-Host line (which happens to be line 9), but only when the Name property of the $Service variable is equal to WinRM. That is what the conditional statement in the -Action parameter's ScriptBlock defines.

# 1. Clean up any existing breakpointsGet-PSBreakpoint | Remove-PSBreakpoint;# 2. Set a PSBreakpoint of type "line" on line #8, but only if the $Service variable's Name property equals 'winrm'Set-PSBreakpoint -Action { if ($Service.Name -eq 'winrm') { break; } } -Line 9 -Script $MyInvocation.MyCommand.Path;# 3. Get a list of Windows Services and iterate over themforeach ($Service in (Get-WmiObject -Class Win32_Service)) {    Write-Host -Object ('Service name is: {0}' -f $Service.Name);}


Yes, there is a simple way to break into the debugger on errors. It looks likeeach time when an error happens the variable StackTrace gets updated. So Iuse this trick: in my profile I have these two functions (switches):

<#.Synopsis    Sets $StackTrace breakpoint.Link    rbps#>function sbps {    $null = Set-PSBreakpoint -Variable StackTrace -Mode Write}<#.Synopsis    Removes $StackTrace breakpoint.Link    sbps#>function rbps {    Get-PSBreakpoint -Variable StackTrace | Remove-PSBreakpoint}

When the first is called then breaking into the debugger on errors is actuallyenabled. On errors StackTrace is written and this triggers the breakpoint.

The second function turns breaking into the debugger on errors off.

In most cases this approach works for me just fine.


UPDATE

In order to use this technique without profile functions a helper script likeDebug-Error.ps1can be used. Ideally, it should should be located in the path, so that commandsDebug-Error and Debug-Error -Off are always available.

See also this blog post.


A few related tickets on Connect:


You could create a function that sets a breakpoint on a variable, then changes the value of the variable.

function Debug-Here {    if(!(Get-PSBreakpoint -Variable DebugHereCount)) {        $SCRIPT:DebugHere= Set-PSBreakpoint -Variable DebugHereCount    }    $DebugHereCount++}

Then call the function from a trap a the top of the script to catch all terminating errors.

trap { Debug-Here }

Or call it from a try-catch statement.

Get-Item DoesNotExist.txt -ErrorAction Stoptry {    $x = 0    $y = 1/$x}catch {    Debug-Here}

Once you are in debug mode, check $Error[0] to see what triggered the breakpoint.