Reputation: 5941
Consider:
Invoke-WebRequest $sumoApiURL -Headers @{"Content-Type"= "application/json"} -Credential $cred -WebSession $webRequestSession -Method post -Body $sumojson -ErrorAction Stop
This throws the following exception:
How can I catch it entirely or at least filter out the "A resource with the same name already exist."?
Using $_.Exception.GetType().FullName
yields
System.Net.WebException
and $_.Exception.Message
gives
The remote server returned an error: (400) Bad Request.
Upvotes: 110
Views: 243449
Reputation: 11
Here's function that will convert everything to nested hashtables with Key values that are string. It's updated version of function I found HERE.
Updates from original function
Example usage: $Error[0] | Convert-OutputObject -Depth 5 -replaceWhitespace -maxStrLgth 1000 | ConvertTo-Json -Depth 5 -Compress
Function Convert-OutputObject {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[AllowNull()]
[object]
$InputObject,
[Parameter(Mandatory = $true)]
[int]
$Depth,
[switch]
$replaceWhitespace = $false,
[int]
$maxStrLgth = 0
)
begin {
$childDepth = $Depth - 1
$isType = {
[CmdletBinding()]
param (
[Object]
$InputObject,
[Type]
$Type
)
if ($InputObject -is $Type) {
return $true
}
$psTypes = @($InputObject.PSTypeNames | ForEach-Object -Process {
$_ -replace '^Deserialized.'
})
$Type.FullName -in $psTypes
}
}
process {
if ($null -eq $InputObject) {
$null
}
elseif ((&$isType -InputObject $InputObject -Type ([Enum])) -and $Depth -ge 0) {
# ToString() gives the human readable value but I thought it better to give some more context behind
# these types.
@{
Type = ($InputObject.PSTypeNames[0] -replace '^Deserialized.')
String = $InputObject.ToString()
Value = [int]$InputObject
}
}
elseif ($InputObject -is [DateTime]) {
# The offset is based on the Kind value
# Unspecified leaves it off
# UTC set it to Z
# Local sets it to the local timezone
$InputObject.ToString('o')
}
elseif (&$isType -InputObject $InputObject -Type ([DateTimeOffset])) {
# If this is a deserialized object (from an executable) we need recreate a live DateTimeOffset
if ($InputObject -isnot [DateTimeOffset]) {
$InputObject = New-Object -TypeName DateTimeOffset $InputObject.DateTime, $InputObject.Offset
}
$InputObject.ToString('o')
}
elseif (&$isType -InputObject $InputObject -Type ([Type])) {
if ($Depth -lt 0) {
$InputObject.FullName
}
else {
# This type is very complex with circular properties, only return somewhat useful properties.
# BaseType might be a string (serialized output), try and convert it back to a Type if possible.
$baseType = $InputObject.BaseType -as [Type]
if ($baseType) {
$baseType = Convert-OutputObject -InputObject $baseType -Depth $childDepth -replaceWhitespace:$replaceWhitespace -maxStrLgth $maxStrLgth
}
@{
Name = $InputObject.Name
FullName = $InputObject.FullName
AssemblyQualifiedName = $InputObject.AssemblyQualifiedName
BaseType = $baseType
}
}
}
elseif ($InputObject -is [string]) {
# Remove whitespace characters and trim length to maxStrLgth
if ($replaceWhitespace)
{
$InputObject = $InputObject -replace '\s+', ' '
}
if ($maxStrLgth -gt 0 -and $InputObject.Length -gt $maxStrLgth)
{
$InputObject = $InputObject.Remove($maxStrLgth)
}
$InputObject
}
elseif (&$isType -InputObject $InputObject -Type ([switch])) {
$InputObject.IsPresent
}
elseif ($InputObject.GetType().IsValueType) {
# We want to display just this value and not any properties it has (if any).
$InputObject
}
elseif ($Depth -lt 0) {
# This must occur after the above to ensure ints and other ValueTypes are preserved as is.
# Remove whitespace characters and trim length to maxStrLgth
# Strong type tempStr. If InputObject was string, would've hit above IF
[string]$tempStr = $InputObject
if ($replaceWhitespace)
{
$tempStr = $InputObject -replace '\s+', ' '
}
if ($maxStrLgth -gt 0 -and $tempStr.Length -gt $maxStrLgth)
{
$tempStr = $tempStr.Remove($maxStrLgth)
}
$tempStr
}
elseif ($InputObject -is [Collections.IList]) {
# Don't create empty hash tables
if ($InputObject.Count -gt 0 )
{
, @(foreach ($obj in $InputObject) {
Convert-OutputObject -InputObject $obj -Depth $childDepth -replaceWhitespace:$replaceWhitespace -maxStrLgth $maxStrLgth
})
}
else
{
$null
}
}
elseif ($InputObject -is [Collections.IDictionary]) {
# Don't create empty hash tables
if ($InputObject.Count -gt 0)
{
$newObj = @{}
# Replicate ConvertTo-Json, props are replaced by keys if they share the same name. We only want ETS
# properties as well.
foreach ($prop in $InputObject.PSObject.Properties) {
if ($prop.MemberType -notin @('AliasProperty', 'ScriptProperty', 'NoteProperty')) {
continue
}
$newObj[$prop.Name] = Convert-OutputObject -InputObject $prop.Value -Depth $childDepth -replaceWhitespace:$replaceWhitespace -maxStrLgth $maxStrLgth
}
foreach ($kvp in $InputObject.GetEnumerator()) {
$newObj[$kvp.Key.ToString()] = Convert-OutputObject -InputObject $kvp.Value -Depth $childDepth -replaceWhitespace:$replaceWhitespace -maxStrLgth $maxStrLgth
}
}
else
{
$newObj = $null
}
$newObj
}
else {
# Don't create empty hash tables
if ($InputObject.PSObject.Properties.Count -gt 0)
{
$newObj = @{}
foreach ($prop in $InputObject.PSObject.Properties) {
$newObj[$prop.Name] = Convert-OutputObject -InputObject $prop.Value -Depth $childDepth -replaceWhitespace:$replaceWhitespace -maxStrLgth $maxStrLgth
}
}
else
{
$newObj = $null
}
$newObj
}
}
}
Upvotes: 0
Reputation: 370
try {1/0} catch { $_ | Format-List * -Force | Out-String }
Results in:
PSMessageDetails :
Exception : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
--- End of inner exception stack trace ---
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject :
CategoryInfo : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.InvocationInfo | Format-List * -Force | Out-String }
Results in:
PSMessageDetails :
Exception : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
--- End of inner exception stack trace ---
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject :
CategoryInfo : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 6
HistoryId : -1
ScriptName :
Line : try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.InvocationInfo | Format-List * -Force | Out-String }
PositionMessage : At line:1 char:6
+ try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.Invocat ...
+ ~~~
PSScriptRoot :
PSCommandPath :
InvocationName :
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force | Out-String ; $Exception.InvocationInfo | Format-List * -Force | Out-String ; for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) { Write-Host ("$i" * 80) ; $Exception | Format-List * -Force | Out-String } }
PSMessageDetails :
Exception : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
--- End of inner exception stack trace ---
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject :
CategoryInfo : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 6
HistoryId : -1
ScriptName :
Line : try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force | Out-String ; $Exception.InvocationInfo | Format-List * -Force | Out-String ; for ($i = 0; $Exception;
$i++, ($Exception = $Exception.InnerException)) { Write-Host ("$i" * 80) ; $Exception | Format-List * -Force | Out-String } }
PositionMessage : At line:1 char:6
+ try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force ...
+ ~~~
PSScriptRoot :
PSCommandPath :
InvocationName :
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
00000000000000000000000000000000000000000000000000000000000000000000000000000000
PSMessageDetails :
Exception : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
--- End of inner exception stack trace ---
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject :
CategoryInfo : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
Upvotes: 20
Reputation: 850
I keep coming back to these questions trying to figure out where exactly the data I'm interested in is buried in what is truly a monolithic ErrorRecord structure. Almost all answers give piecemeal instructions on how to pull certain bits of data.
But I've found it immensely helpful to dump the entire object with ConvertTo-Json
so that I can visually see LITERALLY EVERYTHING in a comprehensible layout.
try {
Invoke-WebRequest...
}
catch {
Write-Host ($_ | ConvertTo-Json)
}
Use ConvertTo-Json
's -Depth
parameter to expand deeper values, but use extreme caution going past the default depth of 2
:P
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json
Upvotes: 17
Reputation: 479
The following worked well for me
try {
asdf
} catch {
$string_err = $_ | Out-String
}
write-host $string_err
The result of this is the following as a string instead of an ErrorRecord object
asdf : The term 'asdf' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\TASaif\Desktop\tmp\catch_exceptions.ps1:2 char:5
+ asdf
+ ~~~~
+ CategoryInfo : ObjectNotFound: (asdf:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Upvotes: 22
Reputation: 200503
Errors and exceptions in PowerShell are structured objects. The error message you see printed on the console is actually a formatted message with information from several elements of the error/exception object. You can (re-)construct it yourself like this:
$formatstring = "{0} : {1}`n{2}`n" +
" + CategoryInfo : {3}`n" +
" + FullyQualifiedErrorId : {4}`n"
$fields = $_.InvocationInfo.MyCommand.Name,
$_.ErrorDetails.Message,
$_.InvocationInfo.PositionMessage,
$_.CategoryInfo.ToString(),
$_.FullyQualifiedErrorId
$formatstring -f $fields
If you just want the error message displayed in your catch
block you can simply echo the current object variable (which holds the error at that point):
try {
...
} catch {
$_
}
If you need colored output use Write-Host
with a formatted string as described above:
try {
...
} catch {
...
Write-Host -Foreground Red -Background Black ($formatstring -f $fields)
}
With that said, usually you don't want to just display the error message as-is in an exception handler (otherwise the -ErrorAction Stop
would be pointless). The structured error/exception objects provide you with additional information that you can use for better error control. For instance you have $_.Exception.HResult
with the actual error number. $_.ScriptStackTrace
and $_.Exception.StackTrace
, so you can display stacktraces when debugging. $_.Exception.InnerException
gives you access to nested exceptions that often contain additional information about the error (top level PowerShell errors can be somewhat generic). You can unroll these nested exceptions with something like this:
$e = $_.Exception
$msg = $e.Message
while ($e.InnerException) {
$e = $e.InnerException
$msg += "`n" + $e.Message
}
$msg
In your case the information you want to extract seems to be in $_.ErrorDetails.Message
. It's not quite clear to me if you have an object or a JSON string there, but you should be able to get information about the types and values of the members of $_.ErrorDetails
by running
$_.ErrorDetails | Get-Member
$_.ErrorDetails | Format-List *
If $_.ErrorDetails.Message
is an object you should be able to obtain the message string like this:
$_.ErrorDetails.Message.message
otherwise you need to convert the JSON string to an object first:
$_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -Expand message
Depending what kind of error you're handling, exceptions of particular types might also include more specific information about the problem at hand. In your case for instance you have a WebException
which in addition to the error message ($_.Exception.Message
) contains the actual response from the server:
PS C:\> $e.Exception | Get-Member TypeName: System.Net.WebException Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj), bool _Exception.E... GetBaseException Method System.Exception GetBaseException(), System.Excep... GetHashCode Method int GetHashCode(), int _Exception.GetHashCode() GetObjectData Method void GetObjectData(System.Runtime.Serialization.S... GetType Method type GetType(), type _Exception.GetType() ToString Method string ToString(), string _Exception.ToString() Data Property System.Collections.IDictionary Data {get;} HelpLink Property string HelpLink {get;set;} HResult Property int HResult {get;} InnerException Property System.Exception InnerException {get;} Message Property string Message {get;} Response Property System.Net.WebResponse Response {get;} Source Property string Source {get;set;} StackTrace Property string StackTrace {get;} Status Property System.Net.WebExceptionStatus Status {get;} TargetSite Property System.Reflection.MethodBase TargetSite {get;}
which provides you with information like this:
PS C:\> $e.Exception.Response IsMutuallyAuthenticated : False Cookies : {} Headers : {Keep-Alive, Connection, Content-Length, Content-T...} SupportsHeaders : True ContentLength : 198 ContentEncoding : ContentType : text/html; charset=iso-8859-1 CharacterSet : iso-8859-1 Server : Apache/2.4.10 LastModified : 17.07.2016 14:39:29 StatusCode : NotFound StatusDescription : Not Found ProtocolVersion : 1.1 ResponseUri : http://www.example.com/ Method : POST IsFromCache : False
Since not all exceptions have the exact same set of properties you may want to use specific handlers for particular exceptions:
try {
...
} catch [System.ArgumentException] {
# handle argument exceptions
} catch [System.Net.WebException] {
# handle web exceptions
} catch {
# handle all other exceptions
}
If you have operations that need to be done regardless of whether an error occured or not (cleanup tasks like closing a socket or a database connection) you can put them in a finally
block after the exception handling:
try {
...
} catch {
...
} finally {
# cleanup operations go here
}
Upvotes: 163
Reputation: 5941
I found it!
Simply print out $Error[0]
for the last error message.
Upvotes: 31