Summary
Stop-Function -Continue executes PowerShell's continue. When no loop encloses the call site inside the command's own body, that flow control does not stop at the function boundary: it unwinds into the caller and consumes an iteration of whatever loop the caller happens to be running in. A user restoring or querying in a foreach over instances silently skips elements when a command hits its error path; under Pester it corrupts the test runner itself, which surfaced for years as the undiagnosable Cannot bind argument to parameter 'ErrorRecord' because it is null crash documented above the skipped StopAt tests.
Two demonstrations, both lab-measured:
The escape bites only the non-EnableException path for -Continue (with EnableException, Stop-Function throws first). -SilentlyContinue executes continue in both modes, so it can escape even under EnableException.
Inventory
An AST sweep over all 1,591 -Continue/-SilentlyContinue sites in public/ and private/functions/ (walking each call's parents for a loop or switch, which also catches continue) found 91 sites with no enclosing loop: 31 in begin blocks, 23 in process, 25 in end, and 12 inside nested helper functions where the continue binds dynamically at the helper's caller and needs per-call-site triage.
Top-level sites per file: Get-DbaRandomizedValue (10), Invoke-DbaPfRelog (10), Set-DbaLogin (6), New-DbaLogShippingPrimarySecondary (5), New-DbaLogShippingSecondaryDatabase (4), New-DbaLogShippingSecondaryPrimary (4), New-DbaAvailabilityGroup (4), Import-DbaSpConfigure (4); two each in Test-DbaLastBackup, Get-DbaRandomizedDataset, New-DbaDbDataGeneratorConfig, Add-DbaReplArticle, New-DbaReplSubscription, Convert-DbaMaskingValue, Import-DbaBinaryFile; one each in Invoke-DbaDbLogShipping, Invoke-DbaDbDecryptObject, Set-DbaAgentOperator, Invoke-DbaDbDataGenerator, New-DbaAzAccessToken, Get-DbaRandomizedType, Stop-DbaExternalProcess, Get-DbaDbObjectTrigger, Get-DbaBackupInformation, Export-DbaDacPackage, Expand-DbaDbLogFile, Copy-DbaDatabase, New-DbaLogShippingPrimaryDatabase, Get-LoginPasswordHash, Get-DbaHelp, Get-DbaWaitResource, Convert-DbaIndexToTable. Nested helpers: export-cert (3), Get-MountPointFromDefaultPath (2), Invoke-ProjectDeployment (2), New-SqlTable (2), Get-JobHistory, Get-MountPointFromPath, New-SqlTableWithInferredSchema. One scriptblock-boundary oddity: Import-DbaPfDataCollectorSetTemplate.
Plan
Work through the list one command per PR (fix = stop-and-return, mirroring #10636/#10637, each with a loop-counter regression test proven red on the unfixed command), families batched only where they share one mechanism. Nested-helper sites get call-site triage first, since a helper called from its command's loop is caught there and may be intentional.
Created by Claude and reviewed by Andreas Jordan.
Summary
Stop-Function -Continueexecutes PowerShell'scontinue. When no loop encloses the call site inside the command's own body, that flow control does not stop at the function boundary: it unwinds into the caller and consumes an iteration of whatever loop the caller happens to be running in. A user restoring or querying in aforeachover instances silently skips elements when a command hits its error path; under Pester it corrupts the test runner itself, which surfaced for years as the undiagnosableCannot bind argument to parameter 'ErrorRecord' because it is nullcrash documented above the skipped StopAt tests.Two demonstrations, both lab-measured:
end-block sites inRestore-DbaDatabase; before the fix, a plain script calling the command lost its own control flow mid-try(a marker line after the call was never reached while thefinallystill ran).begin-block validation inGet-DbaDatabase: aforeachover three elements completed zero iterations against the unfixed command, because the escapedcontinueconsumed one per call.The escape bites only the non-
EnableExceptionpath for-Continue(withEnableException,Stop-Functionthrows first).-SilentlyContinueexecutescontinuein both modes, so it can escape even underEnableException.Inventory
An AST sweep over all 1,591
-Continue/-SilentlyContinuesites inpublic/andprivate/functions/(walking each call's parents for a loop orswitch, which also catchescontinue) found 91 sites with no enclosing loop: 31 inbeginblocks, 23 inprocess, 25 inend, and 12 inside nested helper functions where thecontinuebinds dynamically at the helper's caller and needs per-call-site triage.Top-level sites per file: Get-DbaRandomizedValue (10), Invoke-DbaPfRelog (10), Set-DbaLogin (6), New-DbaLogShippingPrimarySecondary (5), New-DbaLogShippingSecondaryDatabase (4), New-DbaLogShippingSecondaryPrimary (4), New-DbaAvailabilityGroup (4), Import-DbaSpConfigure (4); two each in Test-DbaLastBackup, Get-DbaRandomizedDataset, New-DbaDbDataGeneratorConfig, Add-DbaReplArticle, New-DbaReplSubscription, Convert-DbaMaskingValue, Import-DbaBinaryFile; one each in Invoke-DbaDbLogShipping, Invoke-DbaDbDecryptObject, Set-DbaAgentOperator, Invoke-DbaDbDataGenerator, New-DbaAzAccessToken, Get-DbaRandomizedType, Stop-DbaExternalProcess, Get-DbaDbObjectTrigger, Get-DbaBackupInformation, Export-DbaDacPackage, Expand-DbaDbLogFile, Copy-DbaDatabase, New-DbaLogShippingPrimaryDatabase, Get-LoginPasswordHash, Get-DbaHelp, Get-DbaWaitResource, Convert-DbaIndexToTable. Nested helpers: export-cert (3), Get-MountPointFromDefaultPath (2), Invoke-ProjectDeployment (2), New-SqlTable (2), Get-JobHistory, Get-MountPointFromPath, New-SqlTableWithInferredSchema. One scriptblock-boundary oddity: Import-DbaPfDataCollectorSetTemplate.
Plan
Work through the list one command per PR (fix = stop-and-
return, mirroring #10636/#10637, each with a loop-counter regression test proven red on the unfixed command), families batched only where they share one mechanism. Nested-helper sites get call-site triage first, since a helper called from its command's loop is caught there and may be intentional.Created by Claude and reviewed by Andreas Jordan.