pub const MAX_VALUE_DEPTH: usize = 64;Expand description
How many nested aggregates a single value may hold before either walk refuses it.
§What it is for
A recursive type is legal — IDL 4.2 §7.4.1.4.4.4.4 for a cycle
through a collection, and the same property in the other form for a
cycle through an @optional member — because every value of one
carries its own terminator: a sequence of length zero, an absent
member. The type is a graph and the value is a tree.
A value whose graph really does have a cycle — a.next = a — has no
terminator anywhere in it, and XTypes 1.3 §7.3.1.2.1.4 says of
exactly that case “if the graph of objects has cycles, it is not
serializable”. Neither walk can see the cycle: a source::Source
hands the encoder one member at a time and a byte stream hands the
decoder one header at a time, and in both directions the shape is
only ever known one step ahead. So the walks count, and a value past
the count is refused by name — encode::ProbeError::ValueTooDeep
— rather than truncated, looped on, or run into the stack. A silent
prefix of an infinite stream is the failure class this project ranks
above every other.
§Why this number
Both walks are recursive descent — one write_aggregate frame or
one read_aggregate frame per level — so the real ceiling is the
thread’s stack, and the limit has to sit below it with room to
spare. Measured 2026-08-26 with the guard raised out of the way, in
an unoptimized build on the 2 MiB stack the test harness gives a
spawned thread, over the Node chain of
matrix/cases/recursive-optional-external:
| direction | deepest chain that completed | overflowed at |
|---|---|---|
| decode | 160 | between 160 and 176 |
| encode | 208 | between 208 and 224 |
The decoder is the tighter of the two and the one that faces a hostile peer, so it sets the number: 64 leaves a factor of about 2.5 against the worst measured case, and more than that in a release build or on a main thread’s larger stack.
It is a bound on legitimate values too, and that is stated rather than hidden: a sample carrying a linked list of more than 64 nodes is refused by this walk. The deepest aggregate nesting in the interop corpus is four. Raising the number means raising the measurement above with it, or making the walks iterative.
It is deliberately not zerodds_ast_layout::plan::MAX_DEPTH, which
bounds a descent through the type graph and answers a different
question: the type of a thousand-node list is two levels deep.