There is no universal limit on nested JSON. Practical caps come from parser defaults, runtime memory, and downstream code that recurses. Deep input usually fails as a stack overflow or an out-of-memory error. First check your parser's default depth, then add an application-level depth check for untrusted input. Prefer streaming or iterative parsing wherever you can.
TL;DR:
- Define depth consistently: Scalars and empty containers count as one level, so test documents exactly at your cap, one below, and one above.
- For untrusted input, count container starts during SAX parsing and abort when the configured cap is exceeded, before building a tree.
- Raising a parser limit can move failure to later recursive operations; test serialization, diffing, and flattening at the new depth.
- Schedule depth scans of stored documents because bulk imports and alternate services can bypass application checks; alert when a table’s daily maximum jumps.
Table of Contents
- What does nesting depth mean and how is it measured?
- Where do nested JSON limits come from?
- Which database and storage limits should you enforce?
- How do you detect depth and validate input safely?
- How do you fix deep JSON: streaming, chunking, and iterative code?
- What do we recommend as defaults?
- Where does datatool.dev help with broken or deeply nested JSON?
- FAQ
- Sources
What does nesting depth mean and how is it measured?
Nesting depth is how many levels of containers sit inside each other. The JSON spec sets no maximum, so every limit you hit is a choice made by a library, a database, or your own code.
You need one consistent definition before you can write a check. MySQL's JSON_DEPTH() gives a clear rule: it returns a document's maximum depth, and scalars and empty containers return 1. A non-empty container is 1 plus the depth of its deepest child.
Here is what that looks like:
| Input | Depth |
|---|---|
| — | 1 |
"text" | 1 |
[] | 1 |
{} | 1 |
{"a": 1} | 2 |
{"a": {"b": 1}} | 3 |
[[[]]] | 4 |
{"a": [1, {"b": []}]} | 4 |
Check the last row. The object is level 1, the array under a is level 2, the object inside the array is level 3, and the empty array under b adds one more level for 4. A scalar counts as a level, so {"a": 1} is depth 2, not 1.
Why does the definition matter? Three reasons:
- Your application check and your database constraint must agree. If one counts scalars and the other does not, a document can pass one gate and fail the other.
- A limit written as "128" means nothing until you know what is being counted.
- Off-by-one errors at the boundary are the most common bug in depth checks. Test depth 127, 128, and 129 explicitly.
When you port a check between languages, compare it against the same fixture files. Do not trust the description alone. A parser that reports "max depth 100" may count the root, may not, and may count only containers or also scalar leaves.
Where do nested JSON limits come from?
Limits come from three places: the parser's structure, the library's configured defaults, and whatever code touches the data after parsing. A document can clear the first and still crash in the third.
Recursive parsers risk stack overflow. A parser that calls itself once per nesting level grows the call stack with the input. Deeply nested input can exhaust the call stack, and the process may die before your error handling runs. As the nlohmann/json docs on parsing untrusted input put it, switching to SAX-style or iterative parsers is the standard fix.
Iterative parsers move the risk to memory. nlohmann/json does not impose a hard nesting limit. It uses heap-allocated stacks to avoid call stack exhaustion, but it stays open to out-of-memory errors when input is excessively deep. Its docs tell developers to implement application-level depth tracking for untrusted input.
Library defaults vary widely. Some examples:
- Jackson ships StreamReadConstraints with a default maximum nesting depth. Its
validateNestingDepthmethod throws aStreamConstraintsExceptionwhen the depth exceeds the configured maximum. You can override the defaults at application start forObjectMapperandJsonFactoryinstances. - Ruby's JSON library has a
max_nestingoption. Exceeding it raises a nesting error, and some libraries leave the check off unless you pass a limit. - kotlinx.serialization exposes
maxNestingDepthon its JSON builder.
Read the docs for your exact version. Defaults change between releases, and the safe move is to set the limit explicitly rather than inherit it.
Downstream operations recurse too. This is the trap. Say you raise the parser's max depth so a legitimate deep document loads. Parsing now succeeds. Then your code calls toString(), serializes the tree back out, diffs two documents, or flattens the structure. Each of those often walks the tree recursively. The parse passed and the stack overflow lands one step later, in code you did not think of as "JSON handling."
The cause-and-effect chain looks like this:
- You raise the parse limit to accept deep input.
- The parser accepts a document deeper than the default limit.
- A recursive serializer or diff function walks it.
- The call stack runs out and the process crashes.
Raising a depth limit is safe only if every later operation on the tree is iterative or has been tested at that depth.
Which database and storage limits should you enforce?
Databases publish their own ceilings, and you should enforce your own lower cap before a document is ever stored. A poisoned document that gets persisted fails every time someone reads it.
MySQL. JSON_DEPTH() works in queries and in constraints. You can reject deep documents at write time with a CHECK constraint:
ALTER TABLE events
ADD CONSTRAINT chk_payload_depth
CHECK (JSON_DEPTH(payload) <= 128);
To find offenders already in a table:
SELECT id, JSON_DEPTH(payload) AS depth
FROM events
WHERE JSON_DEPTH(payload) > :depth_limit
ORDER BY depth DESC;
SQL Server. Microsoft's JSON data type documentation lists a limit of 128 nested levels in a JSON document in its size limitations table. It also lists other storage limits, including per-string and per-key sizes and the number of elements.
Oracle. Oracle's JSON restrictions document a maximum number of nesting levels for JSON input, and the example it notes is 1000.
Elasticsearch. The nested field type has limits on nested fields, nested parents, and total nested objects per document. They exist to protect memory and indexing performance, and the defaults guard against excessive nested objects.
Notice the spread: 128 in one engine, 1000 in another. If you move data between systems, the strictest limit in the pipeline is your real limit. Enforce your own cap at the database layer so one bad document cannot reach a consumer that handles it badly.
How do you detect depth and validate input safely?
Measure depth at three points: in storage, during parsing, and over time. Each catches a different class of problem.
Audit stored data. Use JSON_DEPTH() together with JSON_LENGTH() to find documents that are deep or wide. The query above lists the deepest rows. Run it on a schedule, not once.
Count during parsing. A SAX-style handler sees every start and end token without building a tree. Increment a counter on each object or array start, decrement on each end, and throw as soon as the counter passes your limit. The parser stops early, so a hostile document costs you almost nothing. Here is the shape in pseudocode:

on_start_container():
depth += 1
if depth > MAX_DEPTH: abort("depth exceeded")
on_end_container():
depth -= 1
Validate shape after parsing. Depth checks say nothing about structure. Once a document passes the depth gate, check its fields. A browser-based tool like the JSON Schema validator on Gizmobench checks documents against Draft 2020-12 and reports error paths, which is handy for debugging a failing payload by hand.
Monitor for drift. Application checks get bypassed by a new code path, a bulk import, or a second service writing to the same table. Periodic scans with JSON_DEPTH() catch what slipped through. Alert on the maximum depth seen per table per day, and a sudden jump tells you a producer changed.
If you receive malformed input as well as deep input, detection is only half the job. Our guide on parsing, validating, and repairing JSON input covers the repair workflow once you know a document is bad.
How do you fix deep JSON: streaming, chunking, and iterative code?
Stop the failure at the earliest point: reject by depth before building a tree, and avoid recursion on anything untrusted. The steps below work in most stacks.
- Use SAX, streaming, or iterative parsers for untrusted input.
- Set a depth limit explicitly. In Jackson, configure
StreamReadConstraintson your factory rather than trusting the default. - Chunk very large nested documents. Process subtrees in isolation and merge the results.
- After raising any parse limit, test
toString(), serialization, diff, and flatten at the new depth. - Add a size cap beside the depth cap, since a wide document can exhaust memory as surely as a deep one.
A failure and its fix
Here is a minimal recursive depth function in Python. It looks harmless:
import json
def depth(node):
if isinstance(node, dict):
return 1 + max((depth(v) for v in node.values()), default=0)
if isinstance(node, list):
return 1 + max((depth(v) for v in node), default=0)
return 1
payload = read_untrusted_input()
tree = json.loads(payload) # may raise RecursionError first
print(depth(tree))
On deep input this breaks. Python's own json.loads can raise RecursionError while parsing, and if the parse succeeds, depth() recurses once per level and hits the interpreter's recursion limit. The root cause is the same in both places: one stack frame per nesting level.
The fix is to count depth while scanning the raw text, with no tree and no recursion. This version tracks depth in a single pass and ignores brackets inside strings:
def check_depth(text, max_depth=128):
depth = 0
in_string = False
escaped = False
for ch in text:
if in_string:
if escaped:
escaped = False
elif ch == "\\":
escaped = True
elif ch == '"':
in_string = False
continue
if ch == '"':
in_string = True
elif ch in "[{":
depth += 1
if depth > max_depth:
raise ValueError(f"depth exceeds {max_depth}")
elif ch in "]}":
depth -= 1
return True
check_depth(payload) # raises ValueError when the configured limit is exceeded, no recursion
Call check_depth first. Only parse the document if it passes. The loop uses constant memory and stops at the first violation, so the attacker's deeply nested payload is rejected as soon as it exceeds the configured limit.
Pro Tip: Write depth tests at the boundary: build documents at exactly your limit, one over, and ten thousand over, then run parse, serialize, and diff on each.
What do we recommend as defaults?
My default for persistent storage is a database CHECK at 128 or lower. That matches the documented SQL Server ceiling, it is far deeper than any legitimate API payload I have seen, and it is shallow enough that recursive code downstream stays safe. If your data truly needs more, raise it deliberately and test every consumer at that depth.
Malformed AI output can include truncated strings, missing brackets, wrapped responses, or unusual nesting. Test for both malformed syntax and excessive depth. Cover both. Your unit tests should include a truncated deep document, a depth-limit boundary case, and a round trip through every serializer you use.
— Gregory
Where does datatool.dev help with broken or deeply nested JSON?
When a deep document is also truncated or malformed, you need repair that does not guess. Our JSON repair tool fixes broken JSON deterministically, and we log our benchmarks so you can check the results. The how-it-works page explains the @datatool/json-heal engine and how it fits into a developer workflow. If AI models are the source of your bad payloads, our post on why AI outputs broken JSON lists the failure modes to expect.
Paste a broken document into the repair tool at Datatool and see the valid JSON it returns.
FAQ
What is the maximum nesting depth of JSON?
The JSON format sets no maximum depth. Limits come from the parser, the database, and your runtime. SQL Server documents 128 nested levels, and Oracle's restrictions page notes 1000 as an example.
How deep can JSON be nested before it crashes a parser?
It depends on the parser's design. A recursive parser can overflow the call stack, while an iterative one avoids that and can instead run out of memory. Check your library's default and set an explicit limit.
How do I limit JSON depth in a database?
In MySQL, add a CHECK constraint that uses JSON_DEPTH(), for example CHECK (JSON_DEPTH(payload) <= 128). The constraint rejects deep documents at write time. Run JSON_DEPTH() queries on a schedule to find rows that predate the constraint.
Does flattening nested JSON remove the depth problem?
Not automatically. A flatten function that recurses can overflow the stack on deep input, even after the parse succeeds. Make flatten iterative, or check depth before you call it.
Should I raise the parser's max depth for legitimate deep data?
Only after you test every downstream step at the new depth. Serializing, diffing, and flattening often recurse and can fail even when parsing passes. Pair the higher limit with a size cap.

