Module version: OSD 26.7.6.1 (PSGallery, published 2026-07-06)
Affects: Invoke-OSDCloud -> Save-MsUpCatDriver -> Get-MsUpCat -> Invoke-CatalogRequest -> [MsUpCatResponse]
Related: #289 ("Unable to get driver for hardware component") appears to be the same code path, undiagnosed.
Symptom
During the "Microsoft Update Catalog Drivers" phase of an OSDCloud deployment, every device produces
six identical, context-free warnings and then a "No Results" line, taking almost exactly 10 minutes
per device:
[12:27:07 PM] Microsoft Update Catalog Drivers
Drivers for PNPClass DiskDrive will be downloaded from Microsoft Update Catalog to C:\Drivers\MsUpCatDrivers
Drivers for PNPClass Net will be downloaded from Microsoft Update Catalog to C:\Drivers\MsUpCatDrivers
WARNING: You cannot call a method on a null-valued expression.
WARNING: You cannot call a method on a null-valued expression.
WARNING: You cannot call a method on a null-valued expression.
WARNING: You cannot call a method on a null-valued expression.
WARNING: You cannot call a method on a null-valued expression.
WARNING: You cannot call a method on a null-valued expression.
[12:37:09 PM] [Save-MsUpCatDriver] No Results: Intel(R) Wi-Fi 6E AX211 160MHz VEN_8086&DEV_7E40
WARNING: You cannot call a method on a null-valued expression.
... x6 ...
[12:47:09 PM] [Save-MsUpCatDriver] No Results: Intel(R) Ethernet Connection (18) I219-LM VEN_8086&DEV_550A
Six warnings per device corresponds exactly to the six fallback searches in Save-MsUpCatDriver
(22H2, 21H2, Vibranium, 1903, 1809, bare). The warning names no function, no URL and no
hardware ID, which makes this very hard to attribute.
Upstream trigger: the catalog no longer returns the element being parsed
Reproduced directly against catalog.update.microsoft.com from a normal Windows 11 host, outside
WinPE, with Invoke-WebRequest -UseBasicParsing:
| Query |
HTTP |
Response time |
ctl00_catalogBody_updateMatches |
ctl00_catalogBody_noResultText |
22H2+Net+PCI\VEN_8086&DEV_550A |
200 |
100.6s |
ABSENT |
ABSENT |
KB5034123 (a KB that definitely exists) |
200 |
100.5s |
ABSENT |
ABSENT |
PCI\VEN_8086&DEV_550A |
200 |
154.5s |
ABSENT |
ABSENT |
A known-good KB search returns no results table either, so this is not a "no matches for this
hardware ID" condition. The only ctl00_catalogBody_* element ids remaining on the page are:
ctl00_catalogBody_ResultsHeaderTable
ctl00_catalogBody_searchString
ctl00_catalogBody_textDetailsPopupBlocked
__EVENTARGUMENT, __EVENTVALIDATION and __VIEWSTATE are still present. Page <title> is still
Microsoft Update Catalog, there is no error page and no 8DDD* error code. Responses consistently
take 100 to 155 seconds.
So the search results table id that the module depends on is gone site-wide, and the "no results"
element used to detect an empty search is gone as well.
Why the module turns that into a 10-minute hang and a cryptic message
Three separate issues compound it.
1. Classes/MsUpCatResponse.Class.ps1 has no null guards (lines 10-15).
MsUpCatResponse($HtmlDoc) {
$Table = $HtmlDoc.GetElementbyId("ctl00_catalogBody_updateMatches")
$this.Rows = $Table.SelectNodes("tr") | Where-Object { $_.Id -ne "headerRow" } # <-- throws when $Table is $null
$this.EventArgument = $HtmlDoc.GetElementbyId("__EVENTARGUMENT").Attributes["value"].Value
$this.EventValidation = $HtmlDoc.GetElementbyId("__EVENTVALIDATION").Attributes["value"].Value
$this.ViewState = $HtmlDoc.GetElementbyId("__VIEWSTATE").Attributes["value"].Value
$this.ViewStateGenerator = $HtmlDoc.GetElementbyId("__VIEWSTATEGENERATOR").Attributes["value"].Value
$NextPageNode = $HtmlDoc.GetElementbyId("ctl00_catalogBody_nextPageLink")
$this.NextPage = if ($null -ne $NextPageNode) { $NextPageNode.InnerText.Trim() } else { $null }
}
NextPage is the only member that is null-checked. This is unchanged on master as of this report.
2. Private/MSCatalog/Invoke-CatalogRequest.ps1 cannot detect this page state, and discards all error context.
The guard at lines 28-31 only recognises two failure shapes:
$NoResults = $HtmlDoc.GetElementbyId("ctl00_catalogBody_noResultText")
$ErrorText = $HtmlDoc.GetElementbyId("errorPageDisplayedError")
if ($null -eq $NoResults -and $null -eq $ErrorText) {
return [MsUpCatResponse]::new($HtmlDoc) # <-- taken, because neither element exists any more
}
Both are absent now, so the page is treated as a valid result set and the constructor throws. The
handler at lines 46-48 then erases every useful detail:
catch {
Write-Warning "$_"
}
There is also no -TimeoutSec on the Invoke-WebRequest at line 25, so each attempt waits out the
catalog's full 100s+ response.
3. Save-MsUpCatDriver retries six times per device with no failure memory.
Public/Functions/MicrosoftCatalog.ps1 lines 705-721 issue six sequential Get-MsUpCat calls per
device. There is no circuit breaker, so a site-wide outage is re-probed for every hardware ID in the
machine. At ~100s per request that is ~10 minutes per device. Public/OSDCloud.ps1 lines 1776-1786
runs this for DiskDrive, Net and SCSIAdapter, so a typical laptop pays this several times over.
Additionally, $Rows = $Rows.Where({...}) at MicrosoftCatalog.ps1 lines 252 and 369 operates on
$Rows = $Res.Rows (line 233) without checking whether $Res is $null.
Suggested fixes
- Null-guard the
MsUpCatResponse constructor. If ctl00_catalogBody_updateMatches is absent,
leave Rows empty rather than throwing. Same for the __VIEWSTATE family.
- In
Invoke-CatalogRequest, treat "no results table" as an empty result rather than a valid
response, add a -TimeoutSec, and include the URI and exception type in the warning instead of
bare "$_".
- Add a per-run circuit breaker so that once the catalog is determined to be unreachable or
unparseable, the remaining lookups short-circuit instead of re-probing per device.
Workaround (verified)
Replacing Invoke-CatalogRequest in the module's own scope with a version that adds a 20s timeout,
detects the missing results table, and latches a "broken" flag reduced the cost from ~10 minutes per
device to 20 seconds once for the entire deployment, with zero warnings. Measured: first call 20.1s,
second call 0s, five subsequent calls 0.03s total.
Environment
- OSD 26.7.6.1
- Deployment host: WinPE (Windows PowerShell 5.1)
- Reproduced independently on Windows 11 with PowerShell 7.6.4, so this is not WinPE specific
Get-MsUpCat also affected directly, not only via Save-MsUpCatDriver
Module version: OSD 26.7.6.1 (PSGallery, published 2026-07-06)
Affects:
Invoke-OSDCloud->Save-MsUpCatDriver->Get-MsUpCat->Invoke-CatalogRequest->[MsUpCatResponse]Related: #289 ("Unable to get driver for hardware component") appears to be the same code path, undiagnosed.
Symptom
During the "Microsoft Update Catalog Drivers" phase of an OSDCloud deployment, every device produces
six identical, context-free warnings and then a "No Results" line, taking almost exactly 10 minutes
per device:
Six warnings per device corresponds exactly to the six fallback searches in
Save-MsUpCatDriver(
22H2,21H2,Vibranium,1903,1809, bare). The warning names no function, no URL and nohardware ID, which makes this very hard to attribute.
Upstream trigger: the catalog no longer returns the element being parsed
Reproduced directly against
catalog.update.microsoft.comfrom a normal Windows 11 host, outsideWinPE, with
Invoke-WebRequest -UseBasicParsing:ctl00_catalogBody_updateMatchesctl00_catalogBody_noResultText22H2+Net+PCI\VEN_8086&DEV_550AKB5034123(a KB that definitely exists)PCI\VEN_8086&DEV_550AA known-good KB search returns no results table either, so this is not a "no matches for this
hardware ID" condition. The only
ctl00_catalogBody_*element ids remaining on the page are:__EVENTARGUMENT,__EVENTVALIDATIONand__VIEWSTATEare still present. Page<title>is stillMicrosoft Update Catalog, there is no error page and no8DDD*error code. Responses consistentlytake 100 to 155 seconds.
So the search results table id that the module depends on is gone site-wide, and the "no results"
element used to detect an empty search is gone as well.
Why the module turns that into a 10-minute hang and a cryptic message
Three separate issues compound it.
1.
Classes/MsUpCatResponse.Class.ps1has no null guards (lines 10-15).NextPageis the only member that is null-checked. This is unchanged onmasteras of this report.2.
Private/MSCatalog/Invoke-CatalogRequest.ps1cannot detect this page state, and discards all error context.The guard at lines 28-31 only recognises two failure shapes:
Both are absent now, so the page is treated as a valid result set and the constructor throws. The
handler at lines 46-48 then erases every useful detail:
There is also no
-TimeoutSecon theInvoke-WebRequestat line 25, so each attempt waits out thecatalog's full 100s+ response.
3.
Save-MsUpCatDriverretries six times per device with no failure memory.Public/Functions/MicrosoftCatalog.ps1lines 705-721 issue six sequentialGet-MsUpCatcalls perdevice. There is no circuit breaker, so a site-wide outage is re-probed for every hardware ID in the
machine. At ~100s per request that is ~10 minutes per device.
Public/OSDCloud.ps1lines 1776-1786runs this for
DiskDrive,NetandSCSIAdapter, so a typical laptop pays this several times over.Additionally,
$Rows = $Rows.Where({...})atMicrosoftCatalog.ps1lines 252 and 369 operates on$Rows = $Res.Rows(line 233) without checking whether$Resis$null.Suggested fixes
MsUpCatResponseconstructor. Ifctl00_catalogBody_updateMatchesis absent,leave
Rowsempty rather than throwing. Same for the__VIEWSTATEfamily.Invoke-CatalogRequest, treat "no results table" as an empty result rather than a validresponse, add a
-TimeoutSec, and include the URI and exception type in the warning instead ofbare
"$_".unparseable, the remaining lookups short-circuit instead of re-probing per device.
Workaround (verified)
Replacing
Invoke-CatalogRequestin the module's own scope with a version that adds a 20s timeout,detects the missing results table, and latches a "broken" flag reduced the cost from ~10 minutes per
device to 20 seconds once for the entire deployment, with zero warnings. Measured: first call 20.1s,
second call 0s, five subsequent calls 0.03s total.
Environment
Get-MsUpCatalso affected directly, not only viaSave-MsUpCatDriver