From 77cc6080029f4b71c4415e6fa7e9251c89433c4e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Aug 2026 21:26:48 +0000 Subject: [PATCH 1/2] fix: Retry the analyzer in the Analyze task PSScriptAnalyzer executes its script rules in parallel against a process-wide, unsynchronised singleton, so concurrent rules can observe half-updated PowerShell session state and crash. The failure has no relationship to the code being analyzed, which is why re-running the job always succeeded. Retry the analyzer call up to three times, rethrowing on the last attempt. The retry is unconditional: the observed crash signatures are symptoms of an upstream race rather than a fixed contract, and matching on them would let a new variant bypass the mitigation. Pass -ErrorAction Stop explicitly rather than relying on build.ps1 setting ErrorActionPreference process-wide, so the try/catch does not depend on an ambient caller preference. Validated over 45 runs of the Analyze task against a reproduction of the race: zero build failures, with the crash occurring and being absorbed. Refs psake/PowerShellBuild#136 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Y2tE5nDWYPfmyWP2yPeWXT --- psakeFile.ps1 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/psakeFile.ps1 b/psakeFile.ps1 index 593b7eb..587e4ef 100644 --- a/psakeFile.ps1 +++ b/psakeFile.ps1 @@ -16,7 +16,29 @@ task Init { task Test -Depends Init, Analyze, Pester -description 'Run test suite' task Analyze -depends Build { - $analysis = Invoke-ScriptAnalyzer -Path $settings.ModuleOutDir -Recurse -Verbose:$false -Settings ([IO.Path]::Combine($env:BHModulePath, 'ScriptAnalyzerSettings.psd1')) + # PSScriptAnalyzer crashes intermittently on an internal race of its own, unrelated to the + # code being analyzed, and a re-run always succeeds. See psake/PowerShellBuild#136. + $analyzerParameters = @{ + Path = $settings.ModuleOutDir + Recurse = $true + Verbose = $false + Settings = [IO.Path]::Combine($env:BHModulePath, 'ScriptAnalyzerSettings.psd1') + ErrorAction = 'Stop' + } + $maximumAttempt = 3 + for ($attempt = 1; $attempt -le $maximumAttempt; $attempt++) { + try { + $analysis = Invoke-ScriptAnalyzer @analyzerParameters + break + } catch { + if ($attempt -eq $maximumAttempt) { + throw + } + Write-Warning "PSScriptAnalyzer failed on attempt $attempt of $maximumAttempt. Retrying." + Start-Sleep -Seconds 2 + } + } + $errors = $analysis | Where-Object {$_.Severity -eq 'Error'} $warnings = $analysis | Where-Object {$_.Severity -eq 'Warning'} if (@($errors).Count -gt 0) { From 4ad9320f2c66f591127aac5c0cbafe8a879697c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Aug 2026 23:22:57 +0000 Subject: [PATCH 2/2] fix: Log the analyzer error detail on each retried attempt The retry warning named only the attempt number, so a crash absorbed before the final attempt left no record of what actually failed. That undercuts the reason the retry is unconditional: a new crash variant would be retried away silently instead of being visible in the log. Include the error id and message in the warning. The id is the useful discriminator - RULE_ERROR marks the upstream rule crash, while anything else, such as PathNotFound, indicates a genuine analyzer failure that happens to be getting retried. Also copy $_ into a variable at the top of the catch block, per the error handling convention in instructions/powershell.instructions.md. Raised in review on #148. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Y2tE5nDWYPfmyWP2yPeWXT --- psakeFile.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/psakeFile.ps1 b/psakeFile.ps1 index 587e4ef..e0329a1 100644 --- a/psakeFile.ps1 +++ b/psakeFile.ps1 @@ -31,10 +31,14 @@ task Analyze -depends Build { $analysis = Invoke-ScriptAnalyzer @analyzerParameters break } catch { + $errorRecord = $_ if ($attempt -eq $maximumAttempt) { - throw + throw $errorRecord } - Write-Warning "PSScriptAnalyzer failed on attempt $attempt of $maximumAttempt. Retrying." + Write-Warning ( + "PSScriptAnalyzer failed on attempt $attempt of $maximumAttempt and will be retried. " + + "[$($errorRecord.FullyQualifiedErrorId)] $($errorRecord.Exception.Message)" + ) Start-Sleep -Seconds 2 } }