Skip to content

Commit 8ff1163

Browse files
committed
fix: enhance error handling with detailed messages and stack trace
1 parent 22ef999 commit 8ff1163

1 file changed

Lines changed: 105 additions & 19 deletions

File tree

powershell/yt-download.ps1

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,30 +79,102 @@ function TerminateWithError {
7979
Play-Sound -Action Error
8080
}
8181

82+
# Repeats the character 50 times to create a separator line
83+
$Line = '-' * 50
84+
8285
if ($ErrorRecord) {
83-
Write-Host "`n $ErrorMessage `n" -BackgroundColor DarkRed -ForegroundColor White
86+
Write-Host "`n$Line" -ForegroundColor Red
87+
Write-Host "`t$ErrorMessage" -ForegroundColor Red
88+
Write-Host "$Line" -ForegroundColor Red
89+
8490

85-
# Step 1: Display primary exception
91+
# Step 1: Display primary exception with target command name if available
8692
$currentException = $ErrorRecord.Exception
8793
$primaryLine = $ErrorRecord.InvocationInfo.ScriptLineNumber
8894

95+
$invocationName = $null
96+
if ($ErrorRecord.InvocationInfo) {
97+
if ($ErrorRecord.InvocationInfo.InvocationName) {
98+
$invocationName = $ErrorRecord.InvocationInfo.InvocationName
99+
}
100+
elseif ($ErrorRecord.InvocationInfo.MyCommand) {
101+
$invocationName = $ErrorRecord.InvocationInfo.MyCommand.Name
102+
}
103+
}
104+
105+
# Fallback for primary ActionPreferenceStopException to catch the root cmdlet name
106+
if (-not $invocationName -and $ErrorRecord.Exception -and $ErrorRecord.Exception.PSObject.Properties['ErrorRecord']) {
107+
$nestedRecord = $ErrorRecord.Exception.ErrorRecord
108+
if ($nestedRecord -and $nestedRecord.InvocationInfo) {
109+
if ($nestedRecord.InvocationInfo.MyCommand) {
110+
$invocationName = $nestedRecord.InvocationInfo.MyCommand.Name
111+
}
112+
elseif ($nestedRecord.InvocationInfo.InvocationName) {
113+
$invocationName = $nestedRecord.InvocationInfo.InvocationName
114+
}
115+
}
116+
}
117+
89118
if ($primaryLine) {
90119
Write-Host "Error at line: $primaryLine" -ForegroundColor Red
91120
}
92121

93122
$lastMessage = $null
94123
if ($currentException) {
95-
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
124+
# Prefix the command name to match native PowerShell behavior
125+
if ($invocationName) {
126+
Write-Host "${invocationName}: " -NoNewline
127+
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
128+
}
129+
else {
130+
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
131+
}
96132
$lastMessage = $currentException.Message
97133
}
98134

99135
# Step 2: Traverse inner exceptions with deduplication logic
100136
while ($currentException.InnerException) {
137+
$parentException = $currentException
101138
$currentException = $currentException.InnerException
102139

103140
$innerLine = $null
104-
if ($currentException.ErrorRecord -and $currentException.ErrorRecord.InvocationInfo) {
105-
$innerLine = $currentException.ErrorRecord.InvocationInfo.ScriptLineNumber
141+
$innerTargetCommand = $null
142+
143+
# Prioritize custom attached PSErrorRecord because it contains the rich catch-block context
144+
$targetRecord = $null
145+
if ($parentException -and $parentException.PSObject.Properties['PSErrorRecord']) {
146+
$targetRecord = $parentException.PSErrorRecord
147+
}
148+
elseif ($currentException.PSObject.Properties['PSErrorRecord']) {
149+
$targetRecord = $currentException.PSErrorRecord
150+
}
151+
elseif ($currentException.ErrorRecord) {
152+
$targetRecord = $currentException.ErrorRecord
153+
}
154+
155+
if ($targetRecord) {
156+
if ($targetRecord.InvocationInfo) {
157+
$innerLine = $targetRecord.InvocationInfo.ScriptLineNumber
158+
if ($targetRecord.InvocationInfo.MyCommand) {
159+
$innerTargetCommand = $targetRecord.InvocationInfo.MyCommand.Name
160+
}
161+
elseif ($targetRecord.InvocationInfo.InvocationName) {
162+
$innerTargetCommand = $targetRecord.InvocationInfo.InvocationName
163+
}
164+
}
165+
166+
# Fallback for ActionPreferenceStopException where the actual cmdlet name is inside the exception's own ErrorRecord
167+
if (-not $innerTargetCommand -and $targetRecord.Exception -and $targetRecord.Exception.PSObject.Properties['ErrorRecord']) {
168+
$nestedRecord = $targetRecord.Exception.ErrorRecord
169+
if ($nestedRecord -and $nestedRecord.InvocationInfo) {
170+
if ($nestedRecord.InvocationInfo.MyCommand) {
171+
$innerTargetCommand = $nestedRecord.InvocationInfo.MyCommand.Name
172+
}
173+
elseif ($nestedRecord.InvocationInfo.InvocationName) {
174+
$innerTargetCommand = $nestedRecord.InvocationInfo.InvocationName
175+
}
176+
}
177+
}
106178
}
107179

108180
# Deduplicate: skip if the message is identical and provides no new line context
@@ -115,21 +187,37 @@ function TerminateWithError {
115187
Write-Host "Error at line: $innerLine" -ForegroundColor Red
116188
}
117189

118-
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
190+
if ($innerTargetCommand) {
191+
Write-Host "${innerTargetCommand}: " -NoNewline
192+
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
193+
}
194+
else {
195+
Write-Host "$($currentException.Message)" -ForegroundColor Yellow
196+
}
119197
$lastMessage = $currentException.Message
120198
}
121199

122-
Write-Host "`n EXIT " -BackgroundColor DarkRed -ForegroundColor White
200+
# Step 3: Display the script call stack to expose the calling function context
201+
if ($ErrorRecord.ScriptStackTrace) {
202+
Write-Host "`nScript Call Stack:" -ForegroundColor DarkGray
203+
$ErrorRecord.ScriptStackTrace -split "`r?`n" | Where-Object { $_.Trim() } | ForEach-Object {
204+
Write-Host " $_" -ForegroundColor Gray
205+
}
206+
}
207+
208+
Write-Host "`nEXIT " -ForegroundColor Red
123209
}
124210
else {
125-
Write-Host "`n ERROR `n" -BackgroundColor DarkRed -ForegroundColor White
211+
Write-Host "`n$Line" -ForegroundColor Red
212+
Write-Host "`t❌ ERROR ❌" -ForegroundColor Red
213+
Write-Host "$Line" -ForegroundColor Red
214+
126215
Write-Host "$ErrorMessage" -ForegroundColor Yellow
127-
Write-Host "`n EXIT " -BackgroundColor DarkRed -ForegroundColor White
216+
Write-Host "`nEXIT " -ForegroundColor Red
128217
}
129218

130219
exit 1
131220
}
132-
133221
<#*==========================================================================
134222
* ℹ PARAMETERS
135223
@@ -772,15 +860,13 @@ public static extern void CoTaskMemFree(IntPtr pv);
772860
}
773861
catch {
774862
# Pass $_.Exception as the InnerException to preserve the original root cause
775-
$exception = [System.Management.Automation.RuntimeException]::new("File manager failed to open.", $_.Exception)
776-
$errorRecord = [System.Management.Automation.ErrorRecord]::new(
777-
$exception,
778-
"FileManagerError",
779-
[System.Management.Automation.ErrorCategory]::NotSpecified,
780-
$null
781-
)
782-
783-
$PSCmdlet.ThrowTerminatingError($errorRecord)
863+
$innerException = $_.Exception
864+
$outerException = New-Object System.Reflection.TargetInvocationException("File manager failed to open.", $innerException)
865+
866+
# Attach the original ErrorRecord to the exception to preserve PowerShell context
867+
Add-Member -InputObject $outerException -NotePropertyName "PSErrorRecord" -NotePropertyValue $_
868+
869+
throw $outerException
784870
}
785871
}
786872

0 commit comments

Comments
 (0)