private/core/Invoke-ZtRetry.ps1

function Invoke-ZtRetry {
    <#
    .SYNOPSIS
        Invokes a scriptblock with retry logic for transient failures.
 
    .DESCRIPTION
        Executes the provided scriptblock, retrying on failure with exponential backoff.
        Designed for wrapping Graph API calls that may fail due to transient server errors, throttling, or timeouts.
 
    .PARAMETER ScriptBlock
        The scriptblock to execute.
 
    .PARAMETER RetryCount
        The maximum number of retry attempts. Default: 5.
 
    .PARAMETER RetryDelay
        The initial delay in seconds between retries. Doubles with each attempt (exponential backoff). Default: 3.
 
    .EXAMPLE
        PS C:\> Invoke-ZtRetry -ScriptBlock { Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType HashTable }
 
        Executes the Graph request with up to 5 retries and exponential backoff on failure.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [scriptblock]
        $ScriptBlock,

        [Parameter()]
        [scriptblock]
        $RetryHandler,

        [Parameter()]
        [ValidateRange(1, 10)]
        [int]
        $RetryCount = 5,

        [Parameter()]
        [ValidateRange(1, 60)]
        [int]
        $RetryDelay = 3
    )

    $ErrorActionPreference = 'Stop'

    $currentDelay = $RetryDelay

    foreach ($attempt in 1..($RetryCount+1)) {
        try {
            return & $ScriptBlock
        }
        catch {
            $isRetryable = Test-ZtRetryableError -ErrorRecord $_

            if (-not $isRetryable) {
                Write-PSFMessage -Level Warning -Message "Non-retryable error encountered: {0}. Failing immediately." -StringValues $_.Exception.Message -ErrorRecord $_ -Tag Retry
                $PSCmdlet.ThrowTerminatingError($_)
            }

            if ($attempt -gt $RetryCount) { # 1 initial attempt + RetryCount retries = attempt number > RetryCount on the final failure
                $PSCmdlet.ThrowTerminatingError($_)
            }

            Write-PSFMessage -Level Warning -Message "Attempt $attempt of $RetryCount failed: {0}. Retrying in $currentDelay seconds..." -StringValues $_.Exception.Message -ErrorRecord $_ -Tag Retry

            if ($RetryHandler) {
                & $RetryHandler $_
            }

            Start-Sleep -Seconds $currentDelay
            $currentDelay = $currentDelay * 2
        }
    }
}