How do I get the body of a web request that returned 400 Bad Request from Invoke-RestMethod

There is a known issue with PowerShell Invoke-WebRequest and Invoke-RestMethod where the shell eats the response body when the status code is an error (4xx or 5xx). Sounds like the JSON content you are looking for is evaporating in just this manner. You can fetch the response body in your catch block using $_.Exception.Response.GetResponseStream()

    try {
    Invoke-RestMethod "https://api.mysite.com/the/endpoint" `
        -Body (ConvertTo-Json $data) `
        -ContentType "application/json" `
        -Headers $DefaultHttpHeaders `
        -Method Post
    }
    catch {
        $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
        $ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
        $streamReader.Close()
    }

    $ErrResp

According to Invoke-RestMethod documentation, cmdlet can return different types depending on the content it receives. Assing cmdlet output to a variable ($resp = Invoke-RestMethod (...)) and then check if the type is HtmlWebResponseObject ($resp.gettype()). Then you'll have many properties at your disposal, like BaseResponse, Content and StatusCode.

If $resp is some other type (string, psobject and most probably null in this case), it seems that error message The remote server returned an error: (400) Bad Request is the response body, only stripped from html (I tested this on some of my methods), maybe even truncated . If you want to extract it, run the cmdlet using common parameter to store the error message: Invoke-RestMethod (...) -ErrorVariable RespErr and you'll have it in $RespErr variable.

EDIT:

Ok, I got it and it was pretty obvious :). Invoke-RestMethod throws an error, so lets just catch it:

try{$restp=Invoke-RestMethod (...)} catch {$err=$_.Exception}
$err | Get-Member -MemberType Property

  TypeName: System.Net.WebException

    Name           MemberType Definition
    ----           ---------- ----------
    Message        Property   string Message {get;}
    Response       Property   System.Net.WebResponse Response {get;}
    Status         Property   System.Net.WebExceptionStatus Status {get;}

Here's all you need, especially in WebResponse object. I listed 3 properties that catch the eye, there's more. Also if you store $_ instead of $_.Exception there could be some properties PowerShell already extracted for you, but I don't expect nothing more meaningful than in .Exception.Response.


$RespErr will have the more details about the BadRequest in my case its

$responce = Invoke-RestMethod -Uri https://localhost:44377/explore/v2/Content -Method Post -Body $PostData -Headers $header -ErrorVariable RespErr;

$RespErr;

{ "error":{ "code":"","message":"The FavoriteName field is required." } }

It looks like it works only in localhost, i tried with my actual server it didn't work.

another way to try is this

    try{
$response = ""
$response = Invoke-WebRequest -Uri https://contentserverint-mhdev.azurewebsites.net/apis/explore/v2/Content?overwrite=true -Method Post -Body $PostData -Headers  $header -ErrorVariable RespErr 
#$response = Invoke-RestMethod -Uri https://localhost:44377/explore/v2/Content?overwrite=true -Method Post -Body $PostData -Headers  $header -ErrorVariable RespErr 
Write-Host "Content created with url="$response.value[0] 

}
catch [System.Net.WebException] {   
        $respStream = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($respStream)
        $respBody = $reader.ReadToEnd() | ConvertFrom-Json
        $respBody;
 }