67

Invoke-RestMethod call returns only very unhelpful exception below and does not (as far as I can tell) allow you to collect the body content (JSON object shown in fiddler trace results). This seems a pretty bad implementation if so because http 500 definition is pretty specific that client should return the body of the response to help troubleshoot... Am I missing something?

invoke-restmethod -method Post -uri "https://api-stage.enviance.com/ver2/EqlService.svc/eql" -Body (ConvertTo-Json $eqlhash)  -Headers @{"Authorization"="Enviance $session"}

invoke-restmethod : The remote server returned an error: (500) Internal Server Error. At line:1 char:9...

Fiddler trace below

HTTP/1.1 500 Internal Server Error Connection: close Date: Thu, 12 Sep 2013 17:35:00 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 EnvApi-Version: 2.0,2.0 EnvApi-Remaining-Calls: 994,994 EnvApi-Remaining-Interval: 2684,2684 Cache-Control: no-cache Pragma: no-cache Expires: -1 Content-Type: text/csv; charset=utf-8

{"errorNumber":0,"message":"Current user has no rights to retrieve data from table 'CustomFieldTemplate'"}

2
  • Same problem in invoke-webrequest Commented Sep 12, 2013 at 22:11
  • Rather cryptic note in "what's new in powershell 4.0 suggests (possibly) that this issue is a bug and has been fixed. technet.microsoft.com/en-us/library/hh857339.aspx#BKMK_bugs Under Notable bug fixes section: The Invoke-RestMethod cmdlet now returns all available results. Commented Sep 12, 2013 at 23:01

9 Answers 9

123

The other answer does get you the response, but you need an additional step to get the actual body of the response, not just the headers. Here is a snippet:

try {
        $result = Invoke-WebRequest ...
}
catch {
        $result = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
}
Sign up to request clarification or add additional context in comments.

Comments

46

Although an old thread, here an answer to the problem with the cmdlets Invoke-WebRequest and Invoke-RestMethod.

This one has bothered me for quite some time. As all 4xx and 5xx responses are generating an exception, you have to catch that one and then you are able to extract the Response from there though. Use it like this:

$resp = try { Invoke-WebRequest ... } catch { $_.Exception.Response }

Now $resp always contains everything you like.

1 Comment

Probably should add -ErrorAction Stop to the Invoke-WebRequest call.
42

This solution no longer works with PowerShell 6 - it does not support GetResponseStream(). Instead use

try {
    $result = Invoke-WebRequest ...
}
catch {
    $_.ErrorDetails.Message
}

I wrote a short helper function to support PowerShell 6 and earlier:

function ParseErrorForResponseBody($Error) {
    if ($PSVersionTable.PSVersion.Major -lt 6) {
        if ($Error.Exception.Response) {  
            $Reader = New-Object System.IO.StreamReader($Error.Exception.Response.GetResponseStream())
            $Reader.BaseStream.Position = 0
            $Reader.DiscardBufferedData()
            $ResponseBody = $Reader.ReadToEnd()
            if ($ResponseBody.StartsWith('{')) {
                $ResponseBody = $ResponseBody | ConvertFrom-Json
            }
            return $ResponseBody
        }
    }
    else {
        return $Error.ErrorDetails.Message
    }
}

try {
    $result = Invoke-WebRequest ...
}
catch {
    ParseErrorForResponseBody($_)
}

4 Comments

Just what I was looking for. Thanks!
Perfect solution. ` _.Exception.Response` gives all details about HTTPResponse but does not provide exact error details. _.ErrorDetails.Message is perfect error display
this seems to accumulate errors from previous runs $Error.ErrorDetails.Message
PowerShell indeed uses $Error as variable to provide a list of recent exceptions. As the function uses a nested scope, that's still fine and the solution works without problems. If you want, rename the variable $Error to $Err to avoid confusion. I kept it like this for better readability.
13

Searching an answer for my problem I found this thread.

These solution worked for me, but I had to add two new line:

 $reader.BaseStream.Position = 0
 $reader.DiscardBufferedData()

Thanks!

1 Comment

I had to do this also, I added these lines before $responseBody = $reader.ReadToEnd();
6

I have been using Invoke-RestMethod exclusively - and since it might be a wrapper for Invoke-WebRequest, the following information might be useful.

You need to catch the exception and then get the response stream from Response like so.

    try
    {
        $response = Invoke-RestMethod Method Get uri "$($uri)" -Headers $headers
        return $response
    }
    catch
    {
        Write-Host "Exception details: "
        $e = $_.Exception
        Write-Host ("`tMessage: " + $e.Message)
        Write-Host ("`tStatus code: " + $e.Response.StatusCode)
        Write-Host ("`tStatus description: " + $e.Response.StatusDescription)

        Write-Host "`tResponse: " -NoNewline
        $memStream = $e.Response.GetResponseStream()
        $readStream = New-Object System.IO.StreamReader($memStream)
        while ($readStream.Peek() -ne -1) {
            Write-Host $readStream.ReadLine()
        }
        $readStream.Dispose();
    }

Reference: https://social.technet.microsoft.com/Forums/windowsserver/en-US/7d838d53-bfd5-4706-b0df-c4bee2d00412/using-memorystream-and-textreader

1 Comment

It worked perfectly for me. My only change was in the line "Write-Host ("`tStatus code: " + $e.Response.StatusCode)"... I added ".value__" to "$e.Response.StatusCode)": "$e.Response.StatusCode.value__" for getting the status code number
5

Please note as an extension to the answer provider by Florial Feldhaus the $_.ErrorDetails.Message is not the original response body. An HTML tag removal regex is used to make the response easier to read github link. Since the stream is disposed on line 1535 there is currently no way to retrieve the original response body.

The work around is to use the dotnet HttpClient to get the original response instead of using the Invoke-WebRequest cmdlet

$url = "http://localhost"
$client = [System.Net.Http.HttpClient]::new()
$request = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Post, $url)
$request.Content = [System.Net.Http.StringContent]::new("Hello World", [System.Text.Encoding]::UTF8, "plain/text")

$result = $client.SendAsync($request).GetAwaiter().GetResult()
$content = $result.Content.ReadAsStringAsync().GetAwaiter().GetResult()

Write-Verbose $content -Verbose

The powershell core github issue is here

1 Comment

There are a few issues with this response, unless the intent was to provide a PowerShell 6/Core answer. 1) This doesn't work in PowerShell 5.x without first loading System.Net.Http.dll assembly class: eg. [void][System.Reflection.Assembly]::LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Net.Http.dll") 2)* Line 3 is missing the $url variable in the [System.Net.Http.HttpRequestMessage]::new() Constructor. 3) Line 4 will generate an Exception within PowerShell 5.x with[System.Net.Http.HttpMethod]::GET verb.
4

Because this is one of the first hits on google, here is answer for powershell core 6.0.0/7.0.0

Invoke-RestMethod and Invoke-WebRequest supports -SkipHttpErrorCheck switch:

This parameter causes the cmdlet to ignore HTTP error statuses and continue to process responses. The error responses are written to the pipeline just as if they were successful.

If this switch is used then Cmdlets does not throw error when failing status code is received.


Also Invoke-RestMethod now has command line parameters -StatusCodeVariable and -ResponseHeadersVariable that can be used to extract and save response status code and headers


-ResponseHeadersVariable was added on v6.0.0 and -SkipHttpErrorCheck/ResponseStatusVariable on v7.0.0

Comments

0

Better Native Handling Now (?)
I'm using Powershell 7.3.9 on Linux & 5.1.19041.3570 on Windows and found the response body is now shown in the error message. So I removed my extra exception handling and let default behavior take over.

Comments

0

I propose this Invoke-RestMethodEx cmdlet for any PowerShell version.

It throws an exception with a message similar to the original one, but tries to suffix it with the response body.

function Invoke-RestMethodEx {
    try {
        return Invoke-RestMethod  @Args
    }
    catch [System.Net.WebException] {
        $OrigEx = $_;
        $WebEx = $_.Exception;

        if ($PSVersionTable.PSVersion.Major -lt 6) {
            # PS < 6
            if ($null -eq $WebEx.Response) {
                throw $OrigEx;
            }
            try {
                $RespStream = $WebEx.Response.GetResponseStream();
                $RespBody = (New-Object System.IO.StreamReader($RespStream)).ReadToEnd();
                $RespStream.Close();
            }
            catch {
                throw $OrigEx;
            }
        }
        else {
            # PS >= 6
            $RespBody = $OrigEx.ErrorDetails.Message;
        }

        throw "Invoke-RestMethodEx: $($WebEx.Message) Body:`r`n${RespBody}";
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.