The following code works (i.e. returns the expected data from the registry), however it throws a non-terminating error due to a lack of permissions to one of the target key's subkeys, which is benign for this use case.
$adapterKeys = $null
$adapterKeys = Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"
$adapters = $null
$adapters = $adapterKeys | Get-ItemProperty
$adapters | Select -First 1
When specifying -ErrorAction "Stop"
, a terminating error is thrown, but no data is returned:
$adapterKeys = $null
$adapterKeys = Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}" -ErrorAction "Stop"
$adapters = $null
$adapters = $adapterKeys | Get-ItemProperty
$adapters | Select -First 1
So I can catch the error with ErrorAction "Stop"
and try{} catch{}
, but this defeats the purpose since the data is no longer returned.
So with these constraints, how can I return the necessary data, but also catch and ignore the error? Note that I don't want to ignore ALL errors, otherwise I would just use -ErrorAction "SilentlyContinue"
.
Is this expected behavior for Get-ChildItem
and/or -ErrorAction "Stop"
? I don't recall ever running into such an issue before.
Just for reference, the error thrown is:
Get-ChildItem : Requested registry access is not allowed.
At line:2 char:16
+ … apterKeys = Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Control\Cla …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (HKEY_LOCAL_MACHINE\…BE10318}\Properties:String) [Get-ChildItem], SecurityException
+ FullyQualifiedErrorId : System.Security.SecurityException,Microsoft.PowerShell.Commands.GetChildItemCommand
This is due to the Properties
subkey of the target registry key being permissioned such that it is inaccessible to even local administrators. As noted, this is irrelevant for this use case because I don't need the data from this Properties
subkey; only from the other subkeys.
One workaround would be to use Get-ItemProperty
directly on all of the target subkeys, thus never encountering the Properties
key or this specific error at all. However I cannot know how many subkeys there will be, and while they likely will always have a consistent, numerical naming convention, I would like to avoid making these assumptions. Also this question is more about what I'm misunderstanding about the behavior of Get-ChildItem
and/or terminating errors.
Edit: some related questions and info I've come across:
-ErrorAction "SilentlyContinue"
and -ErrorVariable
for post-processing errors, however this doesn't make a lot of sense to me.trap
keyword, which I've never heard of, nor had a use for before.FWIW, my target environment is Windows PowerShell 5.1, although I have the same issue on PowerShell 7+, and on the latest versions of both Win10 and Win11.
Is this expected behavior for
Get-ChildItem
and/or-ErrorAction "Stop"
?
Yes: -ErrorAction Stop
by design raises a script-terminating (runspace-terminating) error upon encountering a non-terminating error and therefore implicitly aborts the pipeline that the command is a part of, resulting in no captured output.
That is, if you try to assign the output from your pipeline to a variable, that variable is never assigned to, even if you catch (trap) the script-terminating error via a try ... catch
statement.[1]
This question suggests using
-ErrorAction "SilentlyContinue"
and-ErrorVariable
for post-processing errors, however this doesn't make a lot of sense to me.
It is, however, the only way to:
# Silence errors, but collect them in self-chosen variable $errs,
# while collecting all successfully enumerated items in $adapterKeys.
$adapterKeys =
Get-ChildItem -ErrorAction SilentlyContinue -ErrorVariable errs "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"
# See if any *unexpected* errors occurred, i.e. errors other than permission-denied ones.
$unexpectedErrors =
$errs | Where-Object { $_.CategoryInfo.Category -ne 'PermissionDenied' }
# Act on unexpected errors
if ($unexpectedErrors) {
throw "Unexpected errors occurred: $unexpectedErrors"
}
[1] Note that, without a variable assignment, partial results print to the host (display) before the first error is encountered and the pipeline is aborted; however, a variable assignment of necessity needs to collect all pipeline output, and this process of collecting is aborted along with the pipeline, and no assignment takes place.
Compare Get-Item ., NoSuch -ErrorAction Stop
with $var = Get-Item ., NoSuch -ErrorAction Stop
: the former prints the output from Get-Item .
to the display and then fails, whereas the latter never completes the assignment to $var
, because the script-terminating error occurs before the pipeline runs to completion.