Powershell: Replace Operator InvalidOperation Exception Message

Created on 15 Apr 2020  路  5Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

There are three variations of the Replacement Operator:

  • -replace is implicitly case-insensitive.
  • -ireplace is explicitly case-insensitive.
  • -creplace is case-sensitive.

The exception message returned when passing three or more comma separated values to -replace, -ireplace or -creplace is counterintuitive:

| Command | Exception Message |
| ----------------------------- |--------------------------------------------------------------------------------------|
| "foo" -replace "o", "e", "e" | InvalidOperation: The -ireplace operator allows only two elements to follow it, not 3. |
| "foo" -ireplace "o", "e", "e" | InvalidOperation: The -ireplace operator allows only two elements to follow it, not 3. |
| "foo" -creplace "o", "e", "e" | InvalidOperation: The -replace operator allows only two elements to follow it, not 3. |

As the table above demonstrates, when either the -replace or -ireplace operator is used, the message content refers to -ireplace. This is not ideal but since -replace and -ireplace are functionally equivalent it's perhaps not a big deal. But what is particularly inappropriate is that the exception message for -creplace refers to -replace.

Expected behavior

The exception message should ideally refer to the respective operator that was used. Else at a minimum, the message returned for the -creplace operator should refer to -creplace.

Environment data

The same result is exhibited in PowerShell 5.1, 7.0.0 and 7.1.0-preview.1, full version details below:

Name                           Value
----                           -----
PSVersion                      5.1.18362.628
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.628
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Name                           Value
----                           -----
PSVersion                      7.0.0
PSEdition                      Core
GitCommitId                    7.0.0
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Name                           Value
----                           -----
PSVersion                      7.1.0-preview.1
PSEdition                      Core
GitCommitId                    7.1.0-preview.1
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
First-Time-Issue Issue-Code Cleanup Resolution-Fixed Up-for-Grabs WG-Engine

Most helpful comment

I'm going to make an attempt to see if this can be improved upon

All 5 comments

I would like to contribute to fix this issue. Please let me know!

I've looked into this a bit. It appears the parameter shown is dependent on if the replace was case sensitive, or not:

if (rList.Count > 2)
{
    // only allow 1 or 2 arguments to -replace
    throw InterpreterError.NewInterpreterException(rval, typeof(RuntimeException), errorPosition,
        "BadReplaceArgument", ParserStrings.BadReplaceArgument, ignoreCase ? "-ireplace" : "-replace", rList.Count);
}

From the comment included, this appears to be expected:

/// <param name="ignoreCase">True for -ireplace/-replace, false for -creplace.</param>

A simple solution to restore consistency may be to rewrite the messages to including both:

if (rList.Count > 2)
{
    // only allow 1 or 2 arguments to -replace
    throw InterpreterError.NewInterpreterException(rval, typeof(RuntimeException), errorPosition,
        "BadReplaceArgument", ParserStrings.BadReplaceArgument, ignoreCase ? "-replace/-ireplace" : "-creplace", rList.Count);
}

This would provide the following error messages:

PS> "foo" -replace "f", "j", "h"
InvalidOperation: The -ireplace/-replace operator allows only two elements to follow it, not 3.

PS> "foo" -ireplace "f", "j", "h"
InvalidOperation: The -ireplace/-replace operator allows only two elements to follow it, not 3.

PS> "foo" -creplace "f", "j", "h"
InvalidOperation: The -creplace operator allows only two elements to follow it, not 3.

Unless anyone has a clever solution to get around this limitation, I'll get a PR opened up 馃榿

I'm going to make an attempt to see if this can be improved upon

:tada:This issue was addressed in #12388, which has now been successfully released as v7.1.0-preview.3.:tada:

Handy links:

Just a thought but perhaps the problem is trying to be too specific. Instead of giving an error message referring to the actual operator (information which in the case of -replace vs -ireplace has been lost), in cases where the error cause is common to all versions of the operator (in this case, too many elements in the second operand) just using the generic operator designation would be sufficient. So in this case the error message would be

InvalidOperation: The replace operator allows only two elements to follow it, not 3.

for all three versions.

In fact, a possibly better message is

InvalidOperation: The replace operator requires exactly two elements in the second operand.

thereby removing any possible confusion about whether 1 element is acceptable (see below) and clarifying the location of the required elements instead of the somewhat nebulous "follow it". Does the replace operator allow another operator to "follow it"? Or a pipeline? Or an output redirection? How about a single element which just happens to be a two element array?

Was this page helpful?
0 / 5 - 0 ratings