internal/Get-MtGitHubRateLimitMessage.ps1
|
function Get-MtGitHubRateLimitMessage { <# .SYNOPSIS Internal: Returns a GitHub rate-limit message for an ErrorRecord, or $null when the error is not a rate-limit response. .DESCRIPTION Mirrors the rate-limit detection in Invoke-MtGitHubRequest so that bootstrap callers (Connect-MtGitHub) can distinguish HTTP 403/429 caused by rate limiting from permission, token, or org-access failures. Returns: - "GitHub API rate limit encountered (HTTP <code>). Resets at: <time>" when the response carries x-ratelimit-remaining = 0 (primary rate limit). - "GitHub secondary rate limit encountered (HTTP <code>). Retry after: <n>s" when the response carries retry-after (secondary rate limit / abuse detection). - "GitHub secondary rate limit encountered (HTTP <code>). Retry after at least 60s." when the response body indicates secondary-limit / abuse-detection wording but no retry-after header is present. - $null for any other error, including 403/429 without rate-limit headers. #> param( [Parameter(Mandatory)] $ErrorRecord ) $code = Get-MtGitHubErrorStatusCode -ErrorRecord $ErrorRecord if ($code -notin 403, 429) { return $null } $response = $null try { if ($null -ne $ErrorRecord.Exception) { $response = $ErrorRecord.Exception.Response } } catch { Write-Debug "Get-MtGitHubRateLimitMessage: $($_.Exception.Message)" } if ($null -eq $response) { return $null } $headers = $null try { $headers = $response.Headers } catch { Write-Debug "Get-MtGitHubRateLimitMessage headers: $($_.Exception.Message)" } if ($null -eq $headers) { return $null } $remaining = Get-MtGitHubResponseHeaderValue -Headers $headers -Name 'x-ratelimit-remaining' $remainingValue = 0 $remainingParsed = $null -ne $remaining -and [int]::TryParse([string]$remaining, [ref]$remainingValue) if ($remainingParsed -and $remainingValue -eq 0) { $reset = Get-MtGitHubResponseHeaderValue -Headers $headers -Name 'x-ratelimit-reset' $resetSeconds = 0L $resetTime = 'unknown' if ($null -ne $reset -and [long]::TryParse([string]$reset, [ref]$resetSeconds)) { try { $resetTime = [DateTimeOffset]::FromUnixTimeSeconds($resetSeconds).LocalDateTime } catch { Write-Debug "Get-MtGitHubRateLimitMessage reset conversion: $($_.Exception.Message)" } } return "GitHub API rate limit encountered (HTTP $code). Resets at: $resetTime" } $retryAfter = Get-MtGitHubResponseHeaderValue -Headers $headers -Name 'retry-after' if ($null -ne $retryAfter) { return "GitHub secondary rate limit encountered (HTTP $code). Retry after: ${retryAfter}s" } # Some secondary-limit responses omit retry-after entirely (e.g. older abuse-detection # responses, or responses where the proxy strips the header). Fall back to body wording - # GitHub's documented messages include phrases like "secondary rate limit" and # "abuse detection mechanism". When matched, recommend the 60-second minimum backoff # documented at https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api. $bodyMessage = Get-MtGitHubErrorMessage -ErrorRecord $ErrorRecord if (-not [string]::IsNullOrEmpty($bodyMessage) -and $bodyMessage -match '(?i)secondary\s+rate\s+limit|abuse\s+detection') { return "GitHub secondary rate limit encountered (HTTP $code). Retry after at least 60s." } return $null } |