Private/Resolve-SCAError.ps1

function Resolve-SCAError {
    <#
    .SYNOPSIS
        Translates a failed CyberArk API call into a structured PowerShell error record.
    .DESCRIPTION
        Extracts the HTTP status, any CyberArk error body (message/code/requestId), and the
        endpoint that was called, then builds a single-line error message plus a detailed
        multi-line message carried on the ErrorRecord. Never includes header values, since those
        may carry the bearer token; only the parsed response body and status metadata are used.
    .PARAMETER ErrorRecord
        The ErrorRecord caught from Invoke-WebRequest / Invoke-RestMethod.
    .PARAMETER Service
        The CyberArk service that was called, for error context.
    .PARAMETER Operation
        The psSCA cmdlet or logical operation name, for error context.
    .PARAMETER Uri
        The request URI that failed. Query string is retained; no header/auth data is present in it.
    .OUTPUTS
        System.Management.Automation.ErrorRecord
    #>

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

        [Parameter(Mandatory)]
        [string]$Service,

        [Parameter(Mandatory)]
        [string]$Operation,

        [Parameter(Mandatory)]
        [string]$Uri
    )

    $statusCode = $null
    $responseBody = $null

    if ($ErrorRecord.Exception -is [Microsoft.PowerShell.Commands.HttpResponseException]) {
        $statusCode = [int]$ErrorRecord.Exception.Response.StatusCode
        if ($ErrorRecord.ErrorDetails -and $ErrorRecord.ErrorDetails.Message) {
            $responseBody = $ErrorRecord.ErrorDetails.Message
        }
    }
    elseif ($ErrorRecord.Exception.Response) {
        try { $statusCode = [int]$ErrorRecord.Exception.Response.StatusCode } catch { $statusCode = $null }
    }

    $serviceMessage = $null
    $errorCode = $null
    $requestId = $null

    if ($responseBody) {
        try {
            $parsed = $responseBody | ConvertFrom-Json -ErrorAction Stop
            $serviceMessage = $parsed.message ?? $parsed.error_description ?? $parsed.error ?? $parsed.Message
            $errorCode = $parsed.code ?? $parsed.errorCode ?? $parsed.error_code
            $requestId = $parsed.requestId ?? $parsed.request_id ?? $parsed.correlationId ?? $parsed.correlation_id
        }
        catch {
            $serviceMessage = Protect-SCASecret -InputObject $responseBody
        }
    }

    if (-not $serviceMessage) {
        $serviceMessage = $ErrorRecord.Exception.Message
    }

    $lines = [System.Collections.Generic.List[string]]::new()
    $lines.Add('Secure Cloud Access API request failed.')
    $lines.Add('')
    if ($statusCode) { $lines.Add("HTTP Status : $statusCode") }
    $lines.Add("Operation : $Operation")
    $lines.Add("Service : $Service")
    $lines.Add("Endpoint : $Uri")
    $lines.Add("Message : $serviceMessage")
    if ($errorCode) { $lines.Add("Error Code : $errorCode") }
    if ($requestId) { $lines.Add("Request ID : $requestId") }

    $message = $lines -join [Environment]::NewLine

    $category = switch ($statusCode) {
        401 { [System.Management.Automation.ErrorCategory]::AuthenticationError }
        403 { [System.Management.Automation.ErrorCategory]::PermissionDenied }
        404 { [System.Management.Automation.ErrorCategory]::ObjectNotFound }
        429 { [System.Management.Automation.ErrorCategory]::LimitsExceeded }
        default { [System.Management.Automation.ErrorCategory]::InvalidOperation }
    }

    $newException = [System.Exception]::new($message, $ErrorRecord.Exception)
    $errorId = "psSCA.$Service.$Operation" + $(if ($statusCode) { ".$statusCode" } else { '' })

    return [System.Management.Automation.ErrorRecord]::new($newException, $errorId, $category, $Uri)
}