Skip to content

7.4.0 Breaking change: Passing -OutFile $null to Invoke-WebRequest fails #20744

Open

Description

@nowakca

Prerequisites

Steps to reproduce

Minimal version

Invoke-WebRequest "http://www.microsoft.com" -OutFile $null

Version that is more closely related to how that sneaks in as a surprise script breaker

function DoTheThing($file) {
  $Params=@{
    Uri="http://www.microsoft.com"
    OutFile=$file
    }
  Invoke-WebRequest @Params
}

DoTheThing

Expected behavior

Should return the results of the web call to the output stream

Actual behavior

Invoke-WebRequest: Cannot validate argument on parameter 'OutFile'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

(Works correctly in 7.2.17 and 7.3.3)



### Error details

```console
Exception             : 
    Type              : System.Management.Automation.ParameterBindingValidationException
    Message           : Cannot validate argument on parameter 'OutFile'. The argument is null or empty. Provide an argument that is not null or empty, and then try t
he command again.
    ParameterName     : OutFile
    ParameterType     : string
    ErrorId           : ParameterArgumentValidationError
    Line              : 1
    Offset            : 46
    CommandInvocation : 
        MyCommand        : Invoke-WebRequest
        ScriptLineNumber : 1
        OffsetInLine     : 1
        HistoryId        : 35
        Line             : Invoke-WebRequest "http://www.microsoft.com" @h
        Statement        : Invoke-WebRequest "http://www.microsoft.com" @h
        PositionMessage  : At line:1 char:1
                           + Invoke-WebRequest "http://www.microsoft.com" @h
                           + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        InvocationName   : Invoke-WebRequest
        PipelineLength   : 1
        PipelinePosition : 1
    ErrorRecord       : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : Cannot validate argument on parameter 'OutFile'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
 command again.
            HResult : -2146233087
        CategoryInfo          : InvalidData: (:) [Invoke-WebRequest], ParentContainsErrorRecordException
        FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
        InvocationInfo        : 
            MyCommand        : Invoke-WebRequest
            ScriptLineNumber : 1
            OffsetInLine     : 46
            HistoryId        : 35
            Line             : Invoke-WebRequest "http://www.microsoft.com" @h
            Statement        : @h
            PositionMessage  : At line:1 char:46
                               + Invoke-WebRequest "http://www.microsoft.com" @h
                               +                                              ~~
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
    TargetSite        : 
        Name          : BindParameter
        DeclaringType : System.Management.Automation.ParameterBinderBase, System.Management.Automation, Version=7.4.0.500, Culture=neutral, PublicKeyToken=31bf3856ad
364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    Data              : System.Collections.ListDictionaryInternal
    InnerException    : 
        Type        : System.Management.Automation.ValidationMetadataException
        ErrorRecord : 
            Exception             : 
                Type    : System.Management.Automation.ParentContainsErrorRecordException
                Message : The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
                HResult : -2146233087
            CategoryInfo          : MetadataError: (:) [], ParentContainsErrorRecordException
            FullyQualifiedErrorId : ArgumentIsNull
        TargetSite  : 
            Name          : Validate
            DeclaringType : System.Management.Automation.ValidateNotNullOrAttributeBase
            MemberType    : Method
            Module        : System.Management.Automation.dll
        Message     : The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
        Source      : System.Management.Automation
        HResult     : -2146233087
        StackTrace  : 
   at System.Management.Automation.ValidateNotNullOrAttributeBase.Validate(Object arguments, EngineIntrinsics engineIntrinsics)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBinding
Flags flags)
    Source            : System.Management.Automation
    HResult           : -2146233087
    StackTrace        : 
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBinding
Flags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, Paramet
erBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParame
ter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindNamedParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandP
arameter parameter)
   at System.Management.Automation.ParameterBinderController.BindNamedParameters(UInt32 parameterSets, Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location ---
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElem
entAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo          : InvalidData: (:) [Invoke-WebRequest], ParameterBindingValidationException
FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
InvocationInfo        : 
    MyCommand        : Invoke-WebRequest
    ScriptLineNumber : 1
    OffsetInLine     : 46
    HistoryId        : 35
    Line             : Invoke-WebRequest "http://www.microsoft.com" @h
    Statement        : @h
    PositionMessage  : At line:1 char:46
                       + Invoke-WebRequest "http://www.microsoft.com" @h
                       +                                              ~~
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Red Hat Enterprise Linux 8.8 (Ootpa)
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No really necessary, literally just paste Invoke-WebRequest "http://www.microsoft.com" -OutFile $null into a shell and watch the error show up

Activity

  1. mklement0 commented on Nov 21, 2023

    @mklement0
    Contributor

    Invoke-RestMethod is affected too.

    A breaking change is always unfortunate, and I don't know if this one was intentional or not, but it's worth noting that the original behavior is questionable:

    • It treats -OutFile $null as if the the -OutFile parameter hadn't been specified and therefore outputs the response to the success output stream.
    • Arguably, it should only ever have done that if -PassThru is also specified (which is tantamount to specifying neither -OutFile nor -PassThru).

    The solution / workaround in the case of the given example function is to only add an OutFile entry to the splatting hashtable if $file is non-$null / non-empty.

    function DoTheThing($file) {
      $Params=@{
        Uri = 'https://example.org'
      }
      if ($file) { $Params.OutFile = $file }
      Invoke-WebRequest @Params
    }
    
    DoTheThing
  2. iSazonov commented on Nov 22, 2023

    @iSazonov
    Collaborator

    Came from #19044

    Discussed in #19043

  3. mklement0 commented on Nov 22, 2023

    @mklement0
    Contributor

    Thanks, Ilya (@iSazonov). I completely forgot about it and now see that I even actively suggested making this change - it hadn't occurred to me that someone would rely on the old behavior.

  4. nowakca commented on Nov 22, 2023

    @nowakca
    Author

    Ilya (@iSazonov) Ah, thank you. That's the logic I expected once I saw the behavior. I see it mentioned on https://github.com/PowerShell/PowerShell/releases/tag/v7.4.0-preview.2 . Sorry I didn't work the search well enough to find that before putting in the false alarm.

    Michael Klement (@mklement0) If you'd have asked me at the time, I wouldn't even have thought I was depending on it... the joys of a decade+ of code floating around. And yeah, the fix/workaround is pretty easy, assuming you can find all the places it may unexpectedly exist over that span of time. That's going to be "fun" to try to suss out.

    So looks like I'm upgrading this system from 7.2 to 7.3 for now, and I'll have a separate project to try to find all the splats and see how they're used.

    Thank you both!

    GitHub
    7.4.0-preview.2 - 2023-03-14 Breaking Changes

    Update some PowerShell APIs to throw ArgumentException instead of ArgumentNullException when the argument is an empty string (#19215) (Thanks @xtqqczz...

  5. fbrosseau commented on Jan 31, 2024

    @fbrosseau

    If anyone finds this issue because of Github Actions / Devops pipeline breaks as of today, it is because the hosted agents started consuming pwsh 7.4. Splatting is the correct solution and will work for old and new powershell (see example a few posts above)

  6. Jaykul commented on Feb 1, 2024

    @Jaykul
    Contributor

    To be clear, re. francis (@fbrosseau)'s comment:

    If anyone finds this issue because of Github Actions / Devops pipeline breaks as of today, it is because the hosted agents started consuming pwsh 7.4. Splatting is the correct solution and will work for old and new powershell (see example a few posts above)

    The only solution is to not pass the parameter unless you're passing a valid file path. Specifically, do not pass $null and do not pass empty string. Using a hashtable and conditionally adding the file parameter (as @mkelement wrote in #issuecomment-1821629851) is the easy way to do that.


    I think we should upgrade that change in the release notes to a breaking change, even though it's unlikely to affect very many people. Does that make sense?

    This also points to a general problem with release notes. Users should not have to collate change logs for all the pre-releases to find out what's in a new major release!

    I believe the only place where all the changes from the pre-releases are in one place is in the What's New in PowerShell 7.4 article -- could there at least be a link to that article in the GitHub Release notes, since those notes currently only mention changes since the previous pre-release?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs-TriageThe issue is new and needs to be triaged by a work group.WG-Cmdletsgeneral cmdlet issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions