X Tutup
The Wayback Machine - https://web.archive.org/web/20250611043521/https://github.com/PowerShell/PowerShell/issues/21345
Skip to content

ErrorAction Ignore broken for Invoke-WebRequest #21345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 tasks done
agowa opened this issue Mar 14, 2024 · 8 comments
Closed
5 tasks done

ErrorAction Ignore broken for Invoke-WebRequest #21345

agowa opened this issue Mar 14, 2024 · 8 comments
Labels
Resolution-Answered The question is answered. WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module

Comments

@agowa
Copy link

agowa commented Mar 14, 2024

Prerequisites

Steps to reproduce

  1. Broken: Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0 -ErrorAction Ignore
  2. Workaround: function test() { [CmdletBinding()] param() ; Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0}; test -ErrorAction Ignore

Edit: It only works with CmdletBinding function-wrapping, if the wrapping function doesn't specify CmdletBinding it is also broken when wrapped.

Expected behavior

Not outputting an exception, both behaving equally

Actual behavior

PS /> Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0 -ErrorAction Ignore
Invoke-WebRequest: 302 Found

Error details

PS /> Get-Error           

Exception             : 
    Type       : Microsoft.PowerShell.Commands.HttpResponseException
    Response   : StatusCode: 302, ReasonPhrase: 'Found', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
                 {
                 x-robots-tag: noindex
                 Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
                 Pragma: private
                 Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0
                 x-versionid: KaGo4r58
                 Location: https://http.codes/?ref=302
                 Access-Control-Allow-Origin: *
                 Access-Control-Allow-Methods: GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS, TRACE
                 Access-Control-Allow-Headers: *
                 Vary: Accept-Encoding
                 X-Request-ID: b4f79d5d-6f85-4445-a538-e131a90309ab
                 X-Cloud-Trace-Context: 06d91c87e7ccc680eb48ddd0e1750517
                 Date: Thu, 14 Mar 2024 20:21:40 GMT
                 Server: Google Frontend
                 Content-Type: text/html; charset=utf-8
                 Expires: -1
                 Content-Length: 9
                 }
    StatusCode : Found
    TargetSite : 
        Name          : ThrowTerminatingError
        DeclaringType : System.Management.Automation.MshCommandRuntime, System.Management.Automation, Version=7.4.1.500, Culture=neutral, 
PublicKeyToken=31bf3856ad364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    Message    : Response status code does not indicate success: 302 (Found).
    Source     : System.Management.Automation
    HResult    : -2146233088
    StackTrace : 
   at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)
TargetObject          : Method: GET, RequestUri: 'https://http.codes/302', Version: 1.1, Content: <null>, Headers:
                        {
                        User-Agent: Mozilla/5.0
                        User-Agent: (Linux; Arch Linux; en-XX-POSIX)
                        User-Agent: PowerShell/7.4.1
                        Accept-Encoding: gzip
                        Accept-Encoding: deflate
                        Accept-Encoding: br
                        }
CategoryInfo          : InvalidOperation: (Method: GET, Reques…cept-Encoding: br
                        }:HttpRequestMessage) [Invoke-WebRequest], HttpResponseException
FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
ErrorDetails          : 302 Found
InvocationInfo        : 
    MyCommand        : Invoke-WebRequest
    ScriptLineNumber : 1
    OffsetInLine     : 1
    HistoryId        : 22
    Line             : Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0 -ErrorAction Ignore
    Statement        : Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0 -ErrorAction Ignore
    PositionMessage  : At line:1 char:1
                       + Invoke-WebRequest -Uri 'https://http.codes/302' -MaximumRedirection 0 …
                       + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    InvocationName   : Invoke-WebRequest
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

Environment data

PS /> $PSVersionTable                                                                          

Name                           Value
----                           -----
PSVersion                      7.4.1
PSEdition                      Core
GitCommitId                    7.4.1
OS                             Arch Linux
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

@agowa agowa added the Needs-Triage The issue is new and needs to be triaged by a work group. label Mar 14, 2024
@mklement0
Copy link
Contributor

The common -ErrorAction parameter only operates on non-terminating errors, whereas what your command emits is a (statement)-terminating one.

To trap terminating errors (both statement- and script-terminating ones), use try / catch (or a trap statement).

Confusingly, the seemingly equivalent $ErrorActionPreference preference variable acts on terminating errors too, and that's what implicitly happened with your test function:
In advanced functions, PowerShell translates common parameters to scope-local preference variables, so with $ErrorActionPreference = 'Ignore' implicitly in effect, the statement-terminating error was effectively silenced.

See also:

@agowa
Copy link
Author

agowa commented Mar 14, 2024

I think this needs to be changed then. As this is very counter-intuitive.
The cleanest way would be to either have -ErrorAction Ignore also ignore terminating errors or have advanced functions handle them in a similar way and only catch non-terminating ones. However that both would probably be breaking changes.

So we'd probably have to add a new parameter like -TerminatingErrorAction or a value like IgnoreAll.

@jhoneill
Copy link

I think this needs to be changed then. As this is very counter-intuitive.

A lot has been written about this, and it's unlikely to change. And the results are not as expected. I remember complaining about the behaviour of the active directory commands more than 10 years ago...

It is not quite as simple either.
In a batch file - and some PS1 files are, effectively batch files. We run commands, a, b and c and if a fails and terminates, either we the batch to stop because b and c won't work or might do something harmful OR we want b and c to run because all three parts are independent - we want what some languages call on error resume next behaviour. Inside compiled code there are two situations: either codes raise a a "serious warning" i.e. prints a message to the error stream and figures out a way to proceed to a normal exit . Or it throws out the anchors, pulls the 'eject' handle and stops other things in the same pipeline. Arguably the former is always preferable.

-ErrorAction and $ErrorActionPreference can mute messages written to the error channel or elevate them to terminating errors. They also prevent throw in a function raising a terminating error (or perhaps more accurately treat that terminating error as caught). What they don't do is stop compiled code stopping the pipeline, and it doesn't take a lot of thought to see that forcing a code that wants to eject to carry on running isn't a good solution.

Often we would LIKE code which goes something like

Invoke-WebRequest    .... -OutFile MyFile -ErrorAction SilentlyContinue
if (-not (test-path  Myfile))  {  handle failure }

or

Invoke-WebRequest    .... -OutFile MyFile -ErrorAction STOP
 something bad if MyFile is missing. 

BUT IWR doesn't do "write-error ... ; goto end" it pulls eject so we need do either

$OldEAP = $ErrorActionPreference
$ErrorActionPreference='SilentlyContinue'; 
invoke-webRequest https://oogle -ErrorAction SilentlyContinue
$ErrorActionPreference = $OldEAP

Which means the the calling script will ignore the error or

try {invoke-webRequest https://oogle -ErrorAction SilentlyContinue} Catch {Write-verbose $_.Exception.InnerException.Message}

Which downgrades it.

It's not simply counter intuitive, there is no way to know if a given cmdlet (a) Prints errors and responds to -error action (b) Ejects and effectively ignores it (c) Uses both in different situations.
i.e You know now that Invoke-WebRequest needs this, but you have no idea what other commands also need it.

More than one person has thought they have the perfect solution to this which turns out to be non-viable.

@StevenBucher98 StevenBucher98 added the WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module label Mar 18, 2024
@SteveL-MSFT
Copy link
Member

For better or worse, the webcmdlets have built in "error checking". I think what you want is:

Invoke-RestMethod -Uri 'https://http.codes/302' -MaximumRedirection 0 -SkipHttpErrorCheck

Which suppresses a feature to cause an error depending on the HTTP status.

@agowa
Copy link
Author

agowa commented Mar 24, 2024

SkipHttpErrorCheck works for me. I didn't look for "Skip" only for "Ignore" ones before posting here. 🤦

@mklement0
Copy link
Contributor

mklement0 commented Mar 25, 2024

@agowa, it's an unfortunate example of where PowerShell's naming conventions - which do not formally cover parameter names - fall short:

There are three different parameter prefixes that express the same fundamental concept: negation or opt-out:

  • -No*
  • -Skip*
  • -Ignore*

To me, these variations could all have been reduced to -No*.

Here's an example of what I believe to be a flawed explanation of why -Skip* and -No* should be distinguished, by a team member: #2993 (comment) (read on for responses).

@SteveL-MSFT SteveL-MSFT added Resolution-Answered The question is answered. and removed Needs-Triage The issue is new and needs to be triaged by a work group. labels Apr 1, 2024
Copy link
Contributor

This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Copy link
Contributor

microsoft-github-policy-service bot commented Apr 3, 2024

📣 Hey @agowa, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution-Answered The question is answered. WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module
Projects
None yet
Development

No branches or pull requests

5 participants
X Tutup