Private/Invoke-MgkWithRetry.ps1

function Invoke-MgkWithRetry {
    <#
    .SYNOPSIS
        Runs a scriptblock with exponential backoff on Graph throttling (429/5xx).
    .NOTES
        Private helper — honours Retry-After when the exception surfaces a
        response, otherwise backs off 2^attempt seconds.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][scriptblock]$ScriptBlock,
        [object[]]$ArgumentList = @(),
        [int]$MaxAttempts = 4
    )

    for ($attempt = 1; ; $attempt++) {
        try { return & $ScriptBlock @ArgumentList }
        catch {
            $status = 0
            if ($_.Exception.PSObject.Properties['ResponseStatusCode']) {
                $status = [int]$_.Exception.ResponseStatusCode
            }
            $retryable = $status -in 429, 502, 503, 504
            if (-not $retryable -or $attempt -ge $MaxAttempts) { throw }

            $delay = [math]::Pow(2, $attempt)
            Write-Verbose "Transient $status from Graph; retrying in ${delay}s (attempt $attempt/$MaxAttempts)"
            Start-Sleep -Seconds $delay
        }
    }
}