Skip to content

Invoke-RestMethod should return the full error response from the remote endpoint #2193

Closed

Description

@1RedOne

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

  1. mattmcnabb commented on Sep 7, 2016

    @mattmcnabb

    Excellent write-up Stephen! I can second Stephen's frustration with this issue!

  2. ngetchell commented on Sep 7, 2016

    @ngetchell

    I've definitely been bitten by this.

  3. AWahlqvist commented on Sep 7, 2016

    @AWahlqvist

    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!

  4. added this to the milestone on Nov 2, 2016
  5. modified the milestones: , on Dec 19, 2016
  6. added
    Issue-Enhancementthe issue is more of a feature request than a bug
    and removed
    Issue-BugIssue has been identified as a bug in the product
    on Feb 11, 2017
  7. vors commented on Feb 11, 2017

    @vors
    Collaborator

    Essentially, if we just remove one line

    response.EnsureSuccessStatusCode();
    

    we will get a pretty decent output.
    For example:

         Reading web response                                                                                                                                          StatusCode        : 400e stream... (Number of bytes read: 229)                                                                                                     StatusDescription : BadRequest                                                                                                                                     Content           : Error in call to API function "team/get_info": Bad HTTP "Content-Type" header: "application/x-www-form-urlencoded".  Expecting one of 
                        "application/json", "application/json; charset=utf-8", "text/plain...
    RawContent        : HTTP/1.1 400 BadRequest
                        Server: nginx
                        Date: Sat, 11 Feb 2017 06:39:47 GMT
                        Connection: keep-alive
                        User-Agent: 
                        
                        Error in call to API function "tea...
    Forms             : 
    Headers           : {[Server, System.String[]], [Date, System.String[]], [Connection, System.String[]], [User-Agent, System.String[]]...}
    Images            : {}
    InputFields       : {}
    Links             : {}
    ParsedHtml        : 
    RawContentLength  : 229
    
  8. vors commented on Feb 11, 2017

    @vors
    Collaborator

    Btw, in the FullCLR version there is no call to it.

  9. added
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    and removed
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    on Feb 11, 2017
  10. SteveL-MSFT commented on Feb 13, 2017

    @SteveL-MSFT
    Member

    Here's the experience I have coded up, feedback welcome:

    PS /home/steve/repos/PowerShell> invoke-webrequest http://httpstat.us/418       
    invoke-webrequest : 418 I'm a teapot
    At line:1 char:1
    + invoke-webrequest http://httpstat.us/418
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (Method: GET, Re...rShell/6.0. 
       0
    }:HttpRequestMessage) [Invoke-WebRequest], I'm a teapot
        + FullyQualifiedErrorId : 418,Microsoft.PowerShell.Commands.InvokeWebReque 
       stCommand
    PS /home/steve/repos/PowerShell> $e.FullyQualifiedErrorId                       
    418,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
  11. SteveL-MSFT commented on Feb 14, 2017

    @SteveL-MSFT
    Member

    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).

  12. vors commented on Feb 15, 2017

    @vors
    Collaborator

    HTTP Status Code is in the front of the FullyQualifiedErrorId

    Not sure that's a good idea.

  13. SteveL-MSFT commented on Feb 15, 2017

    @SteveL-MSFT
    Member

    sergei (@vors), open to suggestions. Could prefix with HttpStatus, but then it's more parsing for scripts that want the actual code

  14. joeyaiello commented on Feb 16, 2017

    @joeyaiello
    Contributor

    @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 Response property 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:

    PS C:\Users\jaiello> $error[1].exception.response
    
    
    IsMutuallyAuthenticated : False
    Cookies                 : {ARRAffinity=ce89bc5d89e3eae7a38e90a2582b973f890225b3bddc47e95c77a1d7831a8a71}
    Headers                 : {X-AspNetMvc-Version, Access-Control-Allow-Origin, Content-Length, Cache-Control...}
    SupportsHeaders         : True
    ContentLength           : 16
    ContentEncoding         :
    ContentType             : text/plain; charset=utf-8
    CharacterSet            : utf-8
    Server                  : Microsoft-IIS/8.0
    LastModified            : 2/15/2017 4:16:36 PM
    StatusCode              : 418
    StatusDescription       : I'm a teapot
    ProtocolVersion         : 1.1
    ResponseUri             : http://httpstat.us/418
    Method                  : GET
    IsFromCache             : False
    

    CoreCLR:

    PS C:\Program Files\PowerShell\6.0.0.15> $error[0].exception | fl -force *
    
    
    Message        : Response status code does not indicate success: 418 (I'm a teapot).
    Data           : {}
    InnerException :
    TargetSite     : System.Net.Http.HttpResponseMessage EnsureSuccessStatusCode()
    StackTrace     :    at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
                        at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
    HelpLink       :
    Source         : System.Net.Http
    HResult        : -2146233088
    
  15. added
    Committee-ReviewedPS-Committee has reviewed this and made a decision
    and removed
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    on Feb 16, 2017
  16. SteveL-MSFT commented on Feb 24, 2017

    @SteveL-MSFT
    Member

    The experience won't be exactly the same as the coreClr types are different, but it's improved and similar:

    PS /home/steve/repos/PowerShell> invoke-webrequest http://httpstat.us/418                                     
    invoke-webrequest : Response status code does not indicate success: 418 (I'm a teapot).
    At line:1 char:1
    + invoke-webrequest http://httpstat.us/418
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (Method: GET, Re...rShell/6.0.0
    }:HttpRequestMessage) [Invoke-WebRequest], I'm a teapot
        + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCo 
       mmand
    PS /home/steve/repos/PowerShell> $error[0].exception.response                                                 
    
    
    Version             : 1.1
    Content             : System.Net.Http.StreamContent
    StatusCode          : 418
    ReasonPhrase        : I'm a teapot
    Headers             : {[Cache-Control, System.String[]], [Server, System.String[]], [X-AspNetMvc-Version, 
                          System.String[]], [X-AspNet-Version, System.String[]]...}
    RequestMessage      : Method: GET, RequestUri: 'http://httpstat.us/418', Version: 1.1, Content: <null>, 
                          Headers:
                          {
                            User-Agent: Mozilla/5.0
                            User-Agent: (Windows NT; Linux 4.4.0-64-generic #85-Ubuntu SMP Mon Feb 20 11:50:30 
                          UTC 2017; en-US)
                            User-Agent: WindowsPowerShell/6.0.0
                          }
    IsSuccessStatusCode : False
  17. abhinavsingh003 commented on Mar 14, 2019

    @abhinavsingh003

    Tried another work around to capture error from a REST API is anyone is trying to capture that:

    function Failure {
                   $Message =  $_.ErrorDetails.Message;
    		$json = $Message | ConvertFrom-Json  #if error you are trying to capture is returned in json
                   Write-Host ("Message: "+ $Message)
    }
    

    With this function loaded in memory, put your try / catch around Invoke-RestMethod and call Failure on error. Hope this helps.

  18. MeGoodGuy commented on Dec 18, 2020

    @MeGoodGuy

    Thanks abhinavsingh003 !

    `        Catch {
                if($_.ErrorDetails.Message) {
                    #WebResponseError
                    Write-Host $_.ErrorDetails.Message;
                } else {
                    #UsualException
                    $_
                }
    
            }`
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Committee-ReviewedPS-Committee has reviewed this and made a decisionIssue-Enhancementthe issue is more of a feature request than a bugResolution-FixedThe issue is fixed.WG-Cmdletsgeneral cmdlet issues

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions