Summary
When a single update object in the search result set throws on a property read, Get-WindowsUpdate / Install-WindowsUpdate terminate with a non-actionable error and return nothing at all. All other pending updates — including security updates that were enumerated successfully — are silently lost.
The underlying COM failure is environmental (a corrupted SoftwareDistribution datastore, see Root cause), but the module's lack of a guard around the property access turns a partial, per-update problem into a total failure of the cmdlet. The user-facing error contains no update title, no property name, and no indication that a Windows Update Agent reset is the remedy.
Install-WindowsUpdate : Der Wert liegt außerhalb des erwarteten Bereichs.
+ CategoryInfo : NotSpecified: (:) [Get-WindowsUpdate], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,PSWindowsUpdate.GetWindowsUpdate
(German locale; English equivalent is Value does not fall within the expected range.)
This appears to be the same failure reported in #14 and in numerous blog posts, where the advice is consistently "reset the Windows Update components" without an explanation of the mechanism. This report provides the stack trace and a minimal reproduction.
Environment
|
|
| Module version |
2.2.1.5 |
| DLL version |
2.2.1.5 |
| OS |
Windows 11 24H2 (x64) |
| OS language |
German (de-DE) |
| PowerShell |
5.1 (Desktop) |
| Execution context |
NT AUTHORITY\SYSTEM, local elevated session via RMM agent (not WinRM) |
| Update source |
Windows Update (default AU service), Microsoft Update also registered |
| WSUS |
none |
Stack trace
Obtained via $_.Exception.StackTrace:
at WUApiLib.IUpdate.get_IsDownloaded()
at PSWindowsUpdate.GetWindowsUpdate.CoreProcessing()
at PSWindowsUpdate.GetWindowsUpdate.ProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
Exception details:
- Type:
System.ArgumentException
HResult: 0x80070057 (E_INVALIDARG)
ParamName: empty string
InnerException: none
The ArgumentException is the CLR's mapping of the COM E_INVALIDARG returned by the property getter — it is not a managed argument-validation failure inside the module.
Affected parameter sets
All three fail identically, with the same stack trace:
Install-WindowsUpdate -AcceptAll -MicrosoftUpdate
Install-WindowsUpdate -AcceptAll -WindowsUpdate
Install-WindowsUpdate -AcceptAll
This is worth stating explicitly because the error is frequently misattributed to -MicrosoftUpdate and the Microsoft Update service registration. On this machine the service was registered correctly the whole time:
Name ServiceID IsDefaultAUService
---- --------- ------------------
Microsoft Update 7971f918-a847-4430-9279-4a52d1efe18d False
DCat Flighting Prod 8b24b027-1dee-babb-9a95-3517dfb9c552 False
Windows Store (Prod) 855e8a7c-ecb4-4ca3-b045-1dfa50104289 False
Windows Update 9482f4b4-e343-43b6-b170-9a65bc822c77 True
Debug output
DEBUG: CmdletStart: get-windowsupdate
DEBUG: ParameterSetName: Default
DEBUG: Set pre search criteria: IsInstalled = 0
DEBUG: Set pre search criteria: IsHidden = 0
DEBUG: Search criteria is: IsInstalled = 0 and IsHidden = 0
DEBUG: <HOST>: Connecting...
DEBUG: Module version: 2.2.1.5
DEBUG: Dll version: 2.2.1.5
DEBUG: UpdateSessionObj mode: Local
DEBUG: ServiceManagerObj mode: Local
DEBUG: Try Default. Set source of updates to Windows Update
VERBOSE: <HOST>: Connecting to Windows Update server. Please wait...
VERBOSE: Found [17] Updates in pre search criteria
DEBUG: Security Intelligence-Update für Microsoft Defender Antivirus – KB2267602 (...)
DEBUG: Update was not filtered
DEBUG: Intel Driver Update (2.1.10501.1)
DEBUG: Update was not filtered
get-windowsupdate : Der Wert liegt außerhalb des erwarteten Bereichs.
Note that the search itself succeeds and returns 17 updates. The failure occurs during result processing, on the third item.
Minimal reproduction (module-independent)
The following demonstrates that the property getter itself throws, outside of PSWindowsUpdate:
$r = (New-Object -ComObject Microsoft.Update.Session).CreateUpdateSearcher().Search("IsInstalled=0 and IsHidden=0")
for ($i = 0; $i -lt $r.Updates.Count; $i++) {
$u = $r.Updates.Item($i)
try {
$v = $u.GetType().InvokeMember('IsDownloaded','GetProperty',$null,$u,$null)
Write-Host ("[{0,2}] IsDownloaded={1,-5} {2}" -f $i, $v, $u.Title)
} catch {
Write-Host ("[{0,2}] THROW {1}" -f $i, $u.Title) -ForegroundColor Red
}
}
Output on the affected machine:
[ 0] IsDownloaded=False Security Intelligence-Update ... KB2267602 (Version 1.455.420.0)
[ 1] THROW Intel Driver Update (2.1.10501.1)
[ 2] THROW Samsung Firmware Driver Update (1.0.0.2)
[ 3] IsDownloaded=False Logitech USB Driver Update (1.1.84.7627)
[ 4] IsDownloaded=False Intel SoftwareComponent Driver Update (267.102.85.0)
...
[16] IsDownloaded=False Lenovo System Driver Update (10.17.2606.3)
Exactly 2 of 17 update objects throw E_INVALIDARG; the remaining 15 return False.
Per-update isolation through the module confirms the same two:
foreach ($u in $r.Updates) {
$id = $u.Identity.UpdateID
try { $null = Get-WindowsUpdate -Criteria "UpdateID='$id'" -ErrorAction Stop
Write-Host "OK $($u.Title)" }
catch { Write-Host "FAIL $($u.Title)" -ForegroundColor Red }
}
Note on reproducing this: $update.IsDownloaded in PowerShell will not reproduce the failure. The PowerShell COM adapter swallows failing property getters and silently returns $null, which is why this is easy to miss during diagnosis. InvokeMember (or any strongly-typed access, as in the compiled cmdlet) is required to observe the exception.
Root cause
The two failing updates were not special in their metadata. A full property dump comparing failing and non-failing driver updates showed no discriminating value — DeploymentAction, DriverClass, DeviceStatus, Categories, DriverVerDate and others all cross-cut both groups:
|
Intel (fail) |
Samsung (fail) |
Logitech (ok) |
Lenovo (ok) |
DeploymentAction |
1 |
1 |
1 |
4 |
DriverClass |
OtherHardware |
Firmware |
OtherHardware |
Firmware |
DeviceStatus |
25174026 |
0 |
25182218 |
25174026 |
| Category |
Drivers |
Drivers |
Drivers |
Drivers |
IsDownloaded is a state query against the local download cache (DataStore.edb under %WINDIR%\SoftwareDistribution). The failure was resolved by resetting that store:
Stop-Service wuauserv, bits, usosvc -Force
Rename-Item C:\Windows\SoftwareDistribution C:\Windows\SoftwareDistribution.bak
Start-Service bits, wuauserv
After the reset, all 17 updates return IsDownloaded = False, and Install-WindowsUpdate -AcceptAll completes normally.
So: inconsistent entries in the local Windows Update datastore cause IUpdate::get_IsDownloaded to return E_INVALIDARG for the affected update objects. This is an environmental condition the module cannot prevent — but it can handle it.
Impact
- A single unreadable property aborts the whole cmdlet. Nothing is returned, not even the updates that were processed successfully before the throw.
- The error message names neither the update nor the property, making self-diagnosis effectively impossible for the average user. This is, I believe, why this failure has such a long tail of unresolved reports.
-NotCategory 'Drivers' is not a viable workaround, since it is applied after the update object is constructed — i.e. after the throw.
- The only working workarounds move the filter into the pre-search criteria, which means excluding entire update classes:
Install-WindowsUpdate -UpdateType Software -AcceptAll
Install-WindowsUpdate -Criteria "IsInstalled = 0 and IsHidden = 0 and Type='Software'" -AcceptAll
Neither is acceptable as a general solution, because it also skips driver and firmware updates that are perfectly healthy.
Suggested fix
Guard the property read in CoreProcessing() and degrade gracefully instead of terminating. Sketch:
private static T SafeGet<T>(Func<T> getter, T fallback, string propertyName,
string updateTitle, Action<string> warn)
{
try { return getter(); }
catch (Exception ex)
{
warn($"Could not read '{propertyName}' for update '{updateTitle}' " +
$"(HRESULT 0x{ex.HResult:X8}). This usually indicates a corrupted Windows Update " +
$"datastore; consider resetting %WINDIR%\\SoftwareDistribution. Update skipped.");
return fallback;
}
}
Used at the call site:
bool isDownloaded = SafeGet(() => update.IsDownloaded, false,
nameof(update.IsDownloaded), title, WriteWarning);
Two points worth considering beyond the immediate fix:
- Skip rather than substitute. For
IsDownloaded a false fallback is harmless for Get-WindowsUpdate, but for Install-WindowsUpdate it would cause a download attempt against an update whose cache state is unknown. Excluding the update from the result set and emitting a warning is probably the safer default.
IsDownloaded is unlikely to be the only affected getter. If the datastore is inconsistent, any state-dependent property on IUpdate may throw. A single defensive accessor applied consistently across the property reads in CoreProcessing() would be more robust than patching this one call site.
Even without any behavioural change, simply including the update title and property name in the error message would be a substantial improvement over the current output.
Summary
When a single update object in the search result set throws on a property read,
Get-WindowsUpdate/Install-WindowsUpdateterminate with a non-actionable error and return nothing at all. All other pending updates — including security updates that were enumerated successfully — are silently lost.The underlying COM failure is environmental (a corrupted
SoftwareDistributiondatastore, see Root cause), but the module's lack of a guard around the property access turns a partial, per-update problem into a total failure of the cmdlet. The user-facing error contains no update title, no property name, and no indication that a Windows Update Agent reset is the remedy.(German locale; English equivalent is
Value does not fall within the expected range.)This appears to be the same failure reported in #14 and in numerous blog posts, where the advice is consistently "reset the Windows Update components" without an explanation of the mechanism. This report provides the stack trace and a minimal reproduction.
Environment
NT AUTHORITY\SYSTEM, local elevated session via RMM agent (not WinRM)Stack trace
Obtained via
$_.Exception.StackTrace:Exception details:
System.ArgumentExceptionHResult:0x80070057(E_INVALIDARG)ParamName: empty stringInnerException: noneThe
ArgumentExceptionis the CLR's mapping of the COME_INVALIDARGreturned by the property getter — it is not a managed argument-validation failure inside the module.Affected parameter sets
All three fail identically, with the same stack trace:
This is worth stating explicitly because the error is frequently misattributed to
-MicrosoftUpdateand the Microsoft Update service registration. On this machine the service was registered correctly the whole time:Debug output
Note that the search itself succeeds and returns 17 updates. The failure occurs during result processing, on the third item.
Minimal reproduction (module-independent)
The following demonstrates that the property getter itself throws, outside of PSWindowsUpdate:
Output on the affected machine:
Exactly 2 of 17 update objects throw
E_INVALIDARG; the remaining 15 returnFalse.Per-update isolation through the module confirms the same two:
Root cause
The two failing updates were not special in their metadata. A full property dump comparing failing and non-failing driver updates showed no discriminating value —
DeploymentAction,DriverClass,DeviceStatus,Categories,DriverVerDateand others all cross-cut both groups:DeploymentActionDriverClassDeviceStatusIsDownloadedis a state query against the local download cache (DataStore.edbunder%WINDIR%\SoftwareDistribution). The failure was resolved by resetting that store:After the reset, all 17 updates return
IsDownloaded = False, andInstall-WindowsUpdate -AcceptAllcompletes normally.So: inconsistent entries in the local Windows Update datastore cause
IUpdate::get_IsDownloadedto returnE_INVALIDARGfor the affected update objects. This is an environmental condition the module cannot prevent — but it can handle it.Impact
-NotCategory 'Drivers'is not a viable workaround, since it is applied after the update object is constructed — i.e. after the throw.Suggested fix
Guard the property read in
CoreProcessing()and degrade gracefully instead of terminating. Sketch:Used at the call site:
Two points worth considering beyond the immediate fix:
IsDownloadedafalsefallback is harmless forGet-WindowsUpdate, but forInstall-WindowsUpdateit would cause a download attempt against an update whose cache state is unknown. Excluding the update from the result set and emitting a warning is probably the safer default.IsDownloadedis unlikely to be the only affected getter. If the datastore is inconsistent, any state-dependent property onIUpdatemay throw. A single defensive accessor applied consistently across the property reads inCoreProcessing()would be more robust than patching this one call site.Even without any behavioural change, simply including the update title and property name in the error message would be a substantial improvement over the current output.