Add friendly explanations for common DISM exit codes (#5087)

* Add friendly explanations for common DISM exit codes

Invoke-WinUtilISODism threw only a raw exit code on DISM failure
(e.g. "DISM add-driver failed with exit code 112"), leaving users to
look up what the number means themselves.

Adds a $knownExitCode lookup table mapping common Windows/DISM exit
codes (disk full, access denied, file/path not found, file in use,
timeout, etc.) to plain-English explanations. When a failure's exit
code is recognized, the thrown message now includes the explanation
in parentheses; unrecognized codes fall back to the original
plain-number message.

Verified the script still parses correctly after the change.

* Add missing period to fallback DISM error message

* Add tests for DISM known/unknown exit code error messages

Adds a test verifying a known exit code (112) produces the friendly
explanation in the thrown message, alongside the existing test
verifying an unrecognized code still falls back to the plain
numeric message.

Verified: all 35 tests in win11creator.Tests.ps1 pass.

* Document DISM friendly error messages in Win11 Creator troubleshooting

Adds a Troubleshooting table row explaining the new DISM error
message format (exit code + explanation in parentheses), with
guidance for the most common cases and a link to Microsoft's full
error code reference for anything not explained.

* Fix duplicate DISM calls in unmapped exit code test

The unmapped-code test called Invoke-WinUtilISOScript twice — once
via Should -Throw, once to capture the message for the no-parens
check — causing $script:dismCalls to double-count. Consolidated to
a single call, checking both the exit code and the absence of a
parenthesized explanation against one captured exception message.

Verified: all 36 tests in win11creator.Tests.ps1 pass.

* Fix broken DISM error code reference link in docs

Replaced the dead windows-hardware/manufacture link with Microsoft's
actual System Error Codes reference page, which DISM exit codes
correspond to.

* Strengthen DISM fallback regression coverage

---------

Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
Kristian
2026-09-29 11:17:51 -05:00
committed by GitHub
co-authored by Chris Titus
parent 8939ecf4b4
commit fc03af421b
3 changed files with 124 additions and 1 deletions
+103
View File
@@ -950,6 +950,109 @@ Describe "Win11 Creator setup media" {
}
}
It "reports a friendly explanation when DISM fails with a known exit code" {
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureKnownCode_$([guid]::NewGuid())"
$installWim = Join-Path $contentRoot 'sources\install.wim'
$template = Get-Content -Path $script:autoUnattendPath -Raw
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
function dism.exe {
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
$script:dismCalls.Add(($Arguments -join '|'))
if ($Arguments -contains '/Get-WimInfo') {
$global:LASTEXITCODE = 0
'Languages : en-US'
'Installation : Client'
'Edition : Professional'
'ProductSuite : Terminal Server'
'ProductType : WinNT'
} elseif ($Arguments -contains '/Mount-Image') {
$global:LASTEXITCODE = 112
'Mount failed'
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
$global:LASTEXITCODE = 0
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
} elseif ($Arguments -contains '/Export-Driver') {
$global:LASTEXITCODE = 0
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
)
} else {
$global:LASTEXITCODE = 0
}
}
try {
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
Set-Content -Path $installWim -Value 'mock-wim'
. $script:isoScriptPath
{ Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional' } |
Should -Throw '*112*Disk is full*'
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
} finally {
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
It "falls back to the plain exit code for an unmapped DISM failure" {
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoMountFailureUnknownCode_$([guid]::NewGuid())"
$installWim = Join-Path $contentRoot 'sources\install.wim'
$template = Get-Content -Path $script:autoUnattendPath -Raw
$script:dismCalls = [System.Collections.Generic.List[string]]::new()
function dism.exe {
param([Parameter(ValueFromRemainingArguments)][string[]]$Arguments)
$script:dismCalls.Add(($Arguments -join '|'))
if ($Arguments -contains '/Get-WimInfo') {
$global:LASTEXITCODE = 0
'Languages : en-US'
'Installation : Client'
'Edition : Professional'
'ProductSuite : Terminal Server'
'ProductType : WinNT'
} elseif ($Arguments -contains '/Mount-Image') {
$global:LASTEXITCODE = 999
'Mount failed'
} elseif ($Arguments -contains '/Get-MountedImageInfo') {
$global:LASTEXITCODE = 0
"Mount Dir : $(Join-Path (Split-Path -Path $contentRoot -Parent) 'wim_mount')"
} elseif ($Arguments -contains '/Export-Driver') {
$global:LASTEXITCODE = 0
Export-WinUtilTestDriverPackage -Arguments $Arguments -Fixtures @(
@{ Path = 'storage_pkg'; Name = 'iaStorAC.inf'; Class = 'System' }
)
} else {
$global:LASTEXITCODE = 0
}
}
try {
New-Item -Path (Split-Path $installWim -Parent) -ItemType Directory -Force | Out-Null
Set-Content -Path $installWim -Value 'mock-wim'
. $script:isoScriptPath
$thrown = $null
try {
Invoke-WinUtilISOScript -ISOContentsDir $contentRoot -AutoUnattendXml $template -InjectCurrentSystemDrivers $true -InstallImagePath $installWim -InstallImageIndex 6 -InstallEditionId 'Professional'
} catch {
$thrown = $_.Exception.Message
}
$thrown | Should -Be 'DISM mount failed with exit code 999.'
@($script:dismCalls | Where-Object { $_ -match '/Get-MountedImageInfo' }).Count | Should -Be 1
@($script:dismCalls | Where-Object { $_ -match '/Unmount-Image\|.*\|/Discard' }).Count | Should -Be 1
} finally {
Remove-Item Function:\dism.exe -ErrorAction SilentlyContinue
Remove-Item -Path $contentRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
It "does not add driver setup artifacts when injection is disabled" {
$contentRoot = Join-Path ([IO.Path]::GetTempPath()) "WinUtilIsoNoDrivers_$([guid]::NewGuid())"