Does the hashtable's Add method for duplicate keys return a terminating or non-terminating exception when using catch?

Viewed 28

When adding a duplicate key to a hashtable using the add method, a MethodInvocationException arises:

50|PS(5.1.19041) C:\Users [220908-11:58:48]> $h = @{}
51|PS(5.1.19041) C:\Users [220908-11:58:51]> $h.add('a',$null)
52|PS(5.1.19041) C:\Users [220908-11:58:53]> $h.add('a',$null)
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'a'  Key being added: 'a'"
At line:1 char:1
+ $h.add('a',$null)
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException

I prepared a couple of functions to test whether this is a non-terminating or terminating error. The results seem conflicting. Here is my first function and test run:

function test-ht {
    [cmdletbinding()]
    param(
        [hashtable]$ht,
        [string]$addKey,
        [string]$addValue,
        [string]$msg
    )

    $ht.Add( $addKey, $addValue )
    write-host "finished $msg"
}

$hash = @{}

test-ht -ht $hash -addKey 'test' -addValue '1' -msg 'trial 1'
test-ht -ht $hash -addKey 'test' -addValue '2' -msg 'trial 1 err'
test-ht -ht $hash -addKey 'test' -addValue '3' -msg 'trial 1 stop' -ErrorAction Stop

This outputs:

finished trial 1
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'test'  Key being added: 'test'"
At line:10 char:5
+     $ht.Add( $addKey, $addValue )
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException

finished trial 1 err
test-ht : Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'test'  Key being added: 'test'"
At line:18 char:1
+ test-ht -ht $hash -addKey 'test' -addValue '3' -msg 'trial 1 stop' -E ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [test-ht], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentException,test-ht

The 2nd command with -msg 'trial 1 err' seems to suggest it is a non-terminating error: the function prints the exception but still writes out its finished message. When I force it to -ErrorAction Stop, the finished message does not appear. This seems like normal behavior for a non-terminating error to me.

Then I run the second test, which is the same as above, except the .Add method is embedded in a try/catch statement:

function test-ht2 {
    [cmdletbinding()]
    param(
        [hashtable]$ht,
        [string]$addKey,
        [string]$addValue,
        [string]$msg
    )

    try {$ht.Add( $addKey, $addValue )} catch{}
    write-host "finished $msg"
}

$hash2 = @{}
test-ht2 -ht $hash2 -addKey 'test' -addValue '1' -msg 'trial 2'
test-ht2 -ht $hash2 -addKey 'test' -addValue '2' -msg 'trial 2 err'

This outputs:

finished trial 2
finished trial 2 err

Here, the catch suppresses the error. This suggests a terminating error to me, as catch does not suppress non-terminating errors, e.g.,

55|PS(5.1.19041) C:\Users [220908-12:05:36]> gci C:/missing; write-host 'finished'
gci : Cannot find path 'C:\missing' because it does not exist.
At line:1 char:1
+ gci C:/missing; write-host 'finished'
+ ~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\missing:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

finished
56|PS(5.1.19041) C:\Users [220908-12:05:42]> try {gci C:/missing; write-host 'finished'} catch {}
gci : Cannot find path 'C:\missing' because it does not exist.
At line:1 char:6
+ try {gci C:/missing; write-host 'finished'} catch {}
+      ~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\missing:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

finished

In summary, in the first run with test-ht, the -msg 'trial 1 err' printed the exception and continued, as if it were non-terminating. In the second run with test-ht2 and the same function calls, the exception is suppressed by catch, as if it were a terminating error.

My questions:

  1. Is this error non-terminating or terminating?
  2. If it is non-terminating, why does the catch suppress the exception message?
  3. If it is terminating, why does test-ht run to completion when adding a duplicate key?
1 Answers

It is terminating:

    $x = @{}
try {
    $x.add('1',1)
    $x.add('1',2)
}
Catch {
    write-error "Terminating error"
}
Write-Error: Terminating error

test-ht runs into an exception if you add the 2nd time the same key:

$x =@{}
test-ht -ht $x -addKey a -addValue 2 -msg "haha"
finished haha
test-ht -ht $x -addKey a -addValue 2 -msg "haha"
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'a'  Key being added:....
finished haha

If you add the key before calling the function and after that call the function with the same key it gives you also the error:

$x =@{a=1}
test-ht -ht $x -addKey a -addValue 2 -msg "haha"
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'a'  Key being added:

test-ht2 is running to completion because there is nothing specified in the catch block which would stop the processing. Add return to the catch block to end processing of the script:

function test-ht2 {
    [cmdletbinding()]
    param(
        [hashtable]$ht,
        [string]$addKey,
        [string]$addValue,
        [string]$msg
    )

    try {$ht.Add( $addKey, $addValue )} catch{return}
    write-host "finished $msg"
}

With retrun in the catch it won't output "finished $msg" anymore.

Related