PowerShell Try/Catch and Retry PowerShell Try/Catch and Retry powershell powershell

PowerShell Try/Catch and Retry


If you frequently need code that retries an action a number of times you could wrap your looped try..catch in a function and pass the command in a scriptblock:

function Retry-Command {    [CmdletBinding()]    Param(        [Parameter(Position=0, Mandatory=$true)]        [scriptblock]$ScriptBlock,        [Parameter(Position=1, Mandatory=$false)]        [int]$Maximum = 5,        [Parameter(Position=2, Mandatory=$false)]        [int]$Delay = 100    )    Begin {        $cnt = 0    }    Process {        do {            $cnt++            try {                $ScriptBlock.Invoke()                return            } catch {                Write-Error $_.Exception.InnerException.Message -ErrorAction Continue                Start-Sleep -Milliseconds $Delay            }        } while ($cnt -lt $Maximum)        # Throw an error after $Maximum unsuccessful invocations. Doesn't need        # a condition, since the function returns upon successful invocation.        throw 'Execution failed.'    }}

Invoke the function like this (default is 5 retries):

Retry-Command -ScriptBlock {    # do something}

or like this (if you need a different amount of retries in some cases):

Retry-Command -ScriptBlock {    # do something} -Maximum 10

The function could be further improved e.g. by making script termination after $Maximum failed attempts configurable with another parameter, so that you can have have actions that will cause the script to stop when they fail, as well as actions whose failures can be ignored.


I adapted @Victor's answer and added:

  • parameter for retries
  • ErrorAction set and restore (or else exceptions do not get caught)
  • exponential backoff delay (I know the OP didn't ask for this, but I use it)
  • got rid of VSCode warnings (i.e. replaced sleep with Start-Sleep)
# [Solution with passing a delegate into a function instead of script block](https://stackoverflow.com/a/47712807/)function Retry(){    param(        [Parameter(Mandatory=$true)][Action]$action,        [Parameter(Mandatory=$false)][int]$maxAttempts = 3    )    $attempts=1        $ErrorActionPreferenceToRestore = $ErrorActionPreference    $ErrorActionPreference = "Stop"    do    {        try        {            $action.Invoke();            break;        }        catch [Exception]        {            Write-Host $_.Exception.Message        }        # exponential backoff delay        $attempts++        if ($attempts -le $maxAttempts) {            $retryDelaySeconds = [math]::Pow(2, $attempts)            $retryDelaySeconds = $retryDelaySeconds - 1  # Exponential Backoff Max == (2^n)-1            Write-Host("Action failed. Waiting " + $retryDelaySeconds + " seconds before attempt " + $attempts + " of " + $maxAttempts + ".")            Start-Sleep $retryDelaySeconds         }        else {            $ErrorActionPreference = $ErrorActionPreferenceToRestore            Write-Error $_.Exception.Message        }    } while ($attempts -le $maxAttempts)    $ErrorActionPreference = $ErrorActionPreferenceToRestore}# function MyFunction($inputArg)# {#     Throw $inputArg# }# #Example of a call:# Retry({MyFunction "Oh no! It happened again!"})# Retry {MyFunction "Oh no! It happened again!"} -maxAttempts 10


Solution with passing a delegate into a function instead of script block:

function Retry([Action]$action){    $attempts=3        $sleepInSeconds=5    do    {        try        {            $action.Invoke();            break;        }        catch [Exception]        {            Write-Host $_.Exception.Message        }                    $attempts--        if ($attempts -gt 0) { sleep $sleepInSeconds }    } while ($attempts -gt 0)    }function MyFunction($inputArg){    Throw $inputArg}#Example of a call:Retry({MyFunction "Oh no! It happend again!"})