Repository navigation
Description
Steps to reproduce
Today, if you use Invoke-RestMethod and have any sort of malformed data in your request, Invoke-RestMethod eats the server response and instead returns a generic PowerShell error message, greatly increasing pain when troubleshooting.
As an example of desired behavior, it would be good if PowerShell surfaced the remote server response instead of just writing an error and dumping the response.
Expected behavior
It would be wonderful if Invoke-RestMethod wrote to an alternate stream or provided rich error data when encountering an error with an endpoint.
For example. note the error data I'm about to get when I used the very useful Failure function provided in this blog post, I'm able to see the full remote server response, which makes troubleshooting this an absolute breeze.
function Failure {
$global:helpme = $body
$global:helpmoref = $moref
$global:result = $_.Exception.Response.GetResponseStream()
$global:reader = New-Object System.IO.StreamReader($global:result)
$global:responseBody = $global:reader.ReadToEnd();
Write-Host -BackgroundColor:Black -ForegroundColor:Red "Status: A system exception was caught."
Write-Host -BackgroundColor:Black -ForegroundColor:Red $global:responsebody
Write-Host -BackgroundColor:Black -ForegroundColor:Red "The request body has been saved to `$global:helpme"
break
}
With this function loaded in memory, I'll run the below code with try/catch and call Failure on error.
$Splat = @{
Method = 'PUT'
Uri = 'https://api.us.onelogin.com/api/1/users/27697924/add_roles'
ContentType = "application/json"
Headers = @{authorization = "bearer:$token" }
Body = @{role_id_array = (143175)}
}
PS C:\git> Invoke-RestMethod @Splat
Status: A system exception was caught.
{"status":{"error":true,"code":400,"type":"bad request","message":"role_id_array should be -\u003e array of positive integers"}}
The request body has been saved to $global:helpme
Actual behavior
In today's PowerShell, all server response data from Invoke-RestMethod is eaten and a generic PowerShell error is presented. In scouring the results from $error[0] I was unable to find the actual server response.
$Splat = @{
Method = 'PUT'
Uri = 'https://api.us.onelogin.com/api/1/users/27697924/add_roles'
ContentType = "application/json"
Headers = @{authorization = "bearer:$token" }
Body = @{role_id_array = (143175)}
}
PS C:\git> Invoke-RestMethod @Splat
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod @Splat
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Environment data
> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.14393.103
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14393.103
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Activity
mattmcnabb commentedon Sep 7, 2016
Excellent write-up Stephen! I can second Stephen's frustration with this issue!
ngetchell commentedon Sep 7, 2016
I've definitely been bitten by this.
AWahlqvist commentedon Sep 7, 2016
Same as the above, also using a helper function to get the error response stream. Hope the fix doesn't break those modules though, but even if it does it would be worth it not having to deal with it everytime.
Great write-up!
vors commentedon Feb 11, 2017
Essentially, if we just remove one line
PowerShell/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs
Line 350 in aa764dd
we will get a pretty decent output.
For example:
vors commentedon Feb 11, 2017
Btw, in the FullCLR version there is no call to it.
SteveL-MSFT commentedon Feb 13, 2017
Here's the experience I have coded up, feedback welcome:
SteveL-MSFT commentedon Feb 14, 2017
The content of the response is stuffed into the Message property of the ErrorDetail and the HTTP Status Code is in the front of the FullyQualifiedErrorId. Response headers are thrown away (not sure if there's any useful case when you receive an error from the server).
vors commentedon Feb 15, 2017
Not sure that's a good idea.
SteveL-MSFT commentedon Feb 15, 2017
sergei (@vors), open to suggestions. Could prefix with
HttpStatus, but then it's more parsing for scripts that want the actual codejoeyaiello commentedon Feb 16, 2017
@PowerShell/powershell-committee (subset of Hemant Mahawar (@HemantMahawar) Jason Shirk (@lzybkr) Kenneth Hansen (@khansen00) and myself) think that the most important piece is that you're able to get at the
Responseproperty of the exception as you can in FullCLR:When running
Invoke-WebRequest http://httpstat.us/418, the behavior currently differs between Core and Full:FullCLR:
CoreCLR:
SteveL-MSFT commentedon Feb 24, 2017
The experience won't be exactly the same as the coreClr types are different, but it's improved and similar:
abhinavsingh003 commentedon Mar 14, 2019
Tried another work around to capture error from a REST API is anyone is trying to capture that:
With this function loaded in memory, put your try / catch around Invoke-RestMethod and call Failure on error. Hope this helps.
MeGoodGuy commentedon Dec 18, 2020
Thanks abhinavsingh003 !