Skip to content

Cap Format-Nicely2 recursion depth to avoid call depth overflow#2830

Merged
nohwnd merged 1 commit into
mainfrom
nohwnd-fix-format-nicely-depth-overflow
Jul 1, 2026
Merged

Cap Format-Nicely2 recursion depth to avoid call depth overflow#2830
nohwnd merged 1 commit into
mainfrom
nohwnd-fix-format-nicely-depth-overflow

Conversation

@nohwnd

@nohwnd nohwnd commented Jul 1, 2026

Copy link
Copy Markdown
Member

Problem

When an assertion fails, Pester formats the actual value for the error message with Format-Nicely2 (src/Format2.ps1). Format-Nicely2Format-Object2 recurses through every property of an object by calling Format-Nicely2 again per property (and likewise Format-Collection2/Format-Hashtable2/Format-Dictionary2 per element).

For deeply nested or self-referential objects this recurses until PowerShell throws:

The script failed due to call depth overflow.

which hides the real assertion result. This happens with SQL Server Management Objects (SMO) stub types (reported in #2828) and, previously, with DirectoryInfo whose Parent/Root point back up the tree (#2474, partially mitigated by a hard-coded property map).

Fix

Thread an [int]$Depth parameter through the four recursive formatters and Format-Nicely2. Each recursive call increments the depth; once past a fixed maximum depth of 10 Format-Nicely2 stops expanding and prints the value's short type instead (e.g. [PSObject]), which is enough for a diagnostic message and cannot recurse further. The limit is a hard-coded constant, not a configurable script variable.

The guard sits after the scalar branches (null/bool/string/type/number/scriptblock, which never recurse) and before the container/object branches (which do), so simple values are always fully formatted. Behavior for normally‑nested values is unchanged — the depth default of 0 makes every existing single‑level call identical to before.

Example, before vs after, for a self‑referential object:

  • Before: The script failed due to call depth overflow.
  • After: Expected value to have type [string], but got [PSObject] PSObject{Name='x'; Self=PSObject{…; Self=[PSObject]}}.

Tests

  • tst/Format2.Tests.ps1: a self-referential object is truncated with a type marker instead of overflowing; values nested past the max depth are cut off.
  • tst/functions/assert/General/Should-HaveType.Tests.ps1: a self-referential actual value now yields a normal assertion failure rather than a call-depth overflow.

989 assert/format tests pass; the custom build analyzer is clean on the changed file.

Fix #2828

@nohwnd nohwnd added this to the 6.0.0 milestone Jul 1, 2026
Failing asserts format the actual value with Format-Nicely2, which
recurses through every property and collection element. Deeply nested
or self-referential objects (such as SMO stub types, or DirectoryInfo
whose Parent/Root point back up the tree) recursed until PowerShell
threw "The script failed due to call depth overflow", which hid the
real assertion result.

Thread a Depth counter through the recursive formatters
(Format-Collection2/Object2/Hashtable2/Dictionary2 and Format-Nicely2)
and stop expanding once past a fixed depth, printing the value's type
instead. Scalars are still fully formatted; only the recursing
container/object branches are guarded.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nohwnd nohwnd force-pushed the nohwnd-fix-format-nicely-depth-overflow branch from 43ba24b to 1518c56 Compare July 1, 2026 20:20
@nohwnd nohwnd merged commit f9de5b4 into main Jul 1, 2026
13 checks passed
@nohwnd nohwnd deleted the nohwnd-fix-format-nicely-depth-overflow branch July 1, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should-HaveType: Fails with The script failed due to call depth overflow

1 participant