PowerShell ConvertTo-Json Depth: Fix Truncated JSON Output

Your PowerShell object converts to JSON fine — until you inspect the output and find deeply nested properties replaced by @{}. The culprit is PowerShell ConvertTo-Json depth: the default is 2, meaning any object more than two levels deep is silently flattened into a string representation. This post explains the depth parameter, when it matters, how to set the right value, and the trade-offs of going too deep.
Quick Answer / TL;DR
Add -Depth 10 (or whatever value your object requires) to ConvertTo-Json. Objects beyond the default depth of 2 are serialized as "@{}" strings rather than nested JSON objects — data loss you may not notice until parsing.
Default -Depth 2 Behavior
The default depth of 2 means the root object is level 1, its immediate properties are level 2, and anything inside those properties is truncated. This catches many admins by surprise because level 1 and 2 convert correctly — the truncation only appears at level 3 and beyond.
# Object with 3 levels of nesting
$config = @{
App = @{
Database = @{
Server = 'sql01'
Port = 1433
Name = 'AppDB'
}
}
}
# Default depth 2 — Database object is truncated
$config | ConvertTo-Json
{
"App": {
"Database": "@{Server=sql01; Port=1433; Name=AppDB}"
}
}
Setting Higher Depth Values
Pass -Depth with a value that exceeds the deepest nesting in your object. For most configuration objects and API payloads, depth 5–10 is sufficient. Use a higher value (20–50) for deeply nested structures like complex AD group membership trees or recursive data models.
# Correct serialization with explicit depth
$config | ConvertTo-Json -Depth 5
{
"App": {
"Database": {
"Server": "sql01",
"Port": 1433,
"Name": "AppDB"
}
}
}
Circular Reference Errors
Setting very high depth values on objects that contain circular references (where an object property points back to a parent) causes ConvertTo-Json to recurse until it hits a stack overflow. PowerShell raises “Serialization depth limit exceeded” or runs out of stack space. Identify circular references with Get-Member and break the cycle before serializing.
# Circular reference example — will cause serialization error at high depth
$parent = @{ Name = 'Parent' }
$child = @{ Name = 'Child'; Parent = $parent }
$parent['Child'] = $child # circular!
# This will throw or produce incorrect output at high depths
try {
$parent | ConvertTo-Json -Depth 20 -ErrorAction Stop
} catch {
Write-Warning "Circular reference detected: $($_.Exception.Message)"
# Fix: serialize without the back-reference
$safe = @{ Name = $parent.Name; Child = @{ Name = $child.Name } }
$safe | ConvertTo-Json -Depth 5
}
Testing Your JSON Output
After serializing, always round-trip test by converting back with ConvertFrom-Json and verifying deep properties survive. This immediately catches truncation issues during development.
$original = @{
Level1 = @{
Level2 = @{
Level3 = @{
Value = 'deep data'
}
}
}
}
$json = $original | ConvertTo-Json -Depth 10
$parsed = $json | ConvertFrom-Json
# Verify deep value survived the round-trip
if ($parsed.Level1.Level2.Level3.Value -eq 'deep data') {
Write-Host 'Round-trip success — depth is sufficient' -ForegroundColor Green
} else {
Write-Warning 'Data lost during serialization — increase -Depth'
}
Compress JSON with -Compress
The -Compress switch removes all whitespace from the JSON output, producing a single-line string. This reduces payload size for API calls and log entries. Combine with a high -Depth value for complete, compact JSON.
# Compact single-line JSON for API payloads
$payload = @{
user = @{ name = 'jsmith'; email = '[email protected]' }
action = 'create'
} | ConvertTo-Json -Depth 5 -Compress
# $payload is now a single compact string
Write-Host "Payload length: $($payload.Length) chars"
Write-Host $payload
Depth vs Object Size Trade-Off
Higher depth increases memory usage and processing time proportionally with object size. For large objects with moderate nesting (e.g., a list of 10,000 users), depth 5 is usually safe and fast. Avoid using -Depth 100 as a catch-all — it provides no protection against circular references and wastes processing time on objects that are only 3–4 levels deep. Choose a depth value that matches your actual object structure.
# Measure impact of different depth values on a large object
$data = 1..1000 | ForEach-Object { @{ Id = $_; Nested = @{ Value = "item$_" } } }
$t2 = (Measure-Command { $data | ConvertTo-Json -Depth 2 }).TotalMilliseconds
$t10 = (Measure-Command { $data | ConvertTo-Json -Depth 10 }).TotalMilliseconds
Write-Host "Depth 2: $([math]::Round($t2,1)) ms"
Write-Host "Depth 10: $([math]::Round($t10,1)) ms"
Common Errors and Fixes
- Objects beyond depth appear as @{} instead of their values. The truncated representation
"@{Key=Value}"is a PowerShell string representation of a hashtable, not valid JSON. When you see this in your JSON output, increase-Depthuntil the nested objects serialize correctly. - Very deep depth on large objects causes memory issues. A list of complex objects serialized with
-Depth 100may consume gigabytes of memory. Profile withMeasure-Commandand a realistic data sample. Use the minimum depth that covers all your data rather than an arbitrary large number.
Related Cmdlets / See Also
Wrapping Up
-Depth 2 is a silent data-loss trap for anyone working with nested objects. Set an explicit depth that matches your object structure, round-trip test with ConvertFrom-Json during development, and use -Compress for API payloads. Avoid very high depth values on large datasets to prevent memory pressure.


