Private/Get-ArgusGraphError.ps1

function Get-ArgusGraphError {
    <#
    .SYNOPSIS
        Extracts the HTTP status code and Retry-After delay from an error thrown by Invoke-MgGraphRequest.

    .DESCRIPTION
        Invoke-MgGraphRequest surfaces HTTP failures as exceptions carrying an HttpResponseMessage
        (Response property) or, depending on the SDK version, only a message such as
        "Response status code does not indicate success: TooManyRequests (Too Many Requests)."
        Both shapes are handled. StatusCode is $null when it cannot be determined.
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        [System.Management.Automation.ErrorRecord] $ErrorRecord
    )

    $status = $null
    $retryAfter = $null
    $names = @{
        BadRequest = 400; Unauthorized = 401; Forbidden = 403; NotFound = 404; UnprocessableEntity = 422; TooManyRequests = 429
        InternalServerError = 500; BadGateway = 502; ServiceUnavailable = 503; GatewayTimeout = 504
    }

    $exception = $ErrorRecord.Exception
    foreach ($candidate in @($exception, $exception.InnerException)) {
        if ($null -eq $candidate) { continue }
        $responseProperty = $candidate.PSObject.Properties['Response']
        if ($null -eq $responseProperty -or $null -eq $responseProperty.Value) { continue }
        $response = $responseProperty.Value
        if ($response.PSObject.Properties['StatusCode']) {
            $status = [int]$response.StatusCode
        }
        if ($response.PSObject.Properties['Headers'] -and $response.Headers -and $response.Headers.PSObject.Properties['RetryAfter']) {
            $header = $response.Headers.RetryAfter
            if ($header -and $header.Delta) { $retryAfter = [math]::Ceiling($header.Delta.TotalSeconds) }
            elseif ($header -and $header.Date) { $retryAfter = [math]::Max(0, [math]::Ceiling(($header.Date - [datetimeoffset]::UtcNow).TotalSeconds)) }
        }
        if ($status) { break }
    }

    $message = [string]$exception.Message
    if (-not $status) {
        if ($message -match '\b(400|401|403|404|422|429|500|502|503|504)\b') { $status = [int]$Matches[1] }
        else {
            foreach ($name in $names.Keys) {
                if ($message -match "\b$name\b") { $status = $names[$name]; break }
            }
        }
    }
    if ($null -eq $retryAfter -and $message -match 'Retry-After[^\d]{0,5}(\d+)') { $retryAfter = [int]$Matches[1] }

    [pscustomobject]@{
        StatusCode = $status
        RetryAfter = $retryAfter
        Message    = $message
        Transient  = $status -in 429, 503, 504
    }
}