langchain-ai/langchain · error · ValueError
{max_depth=} exceeded, unable to combine dicts.
Error message
{max_depth=} exceeded, unable to combine dicts. What it means
Raised by `_dict_int_op` in `langchain_core.utils.usage` when combining two nested dicts of integers (e.g. accumulating token `UsageMetadata` across a run) exceeds the `max_depth` limit (default 100). It is a guard against pathological or self-referential structures that would recurse forever. Combining aborts with `ValueError`; no partial sum is returned.
Source
Thrown at libs/core/langchain_core/utils/usage.py:39
Supports nested dictionaries.
Args:
left: First dictionary to combine.
right: Second dictionary to combine.
op: Binary operation function to apply to integer values.
default: Default value to use when a key is missing from a dictionary.
depth: Current recursion depth (used internally).
max_depth: Maximum recursion depth (to prevent infinite loops).
Returns:
A new dictionary with combined values.
Raises:
ValueError: If `max_depth` is exceeded or if value types are not supported.
"""
if depth >= max_depth:
msg = f"{max_depth=} exceeded, unable to combine dicts."
raise ValueError(msg)
combined: dict[str, Any] = {}
for k in set(left).union(right):
if isinstance(left.get(k, default), int) and isinstance(
right.get(k, default), int
):
combined[k] = op(left.get(k, default), right.get(k, default))
elif isinstance(left.get(k, {}), dict) and isinstance(right.get(k, {}), dict):
combined[k] = _dict_int_op(
left.get(k, {}),
right.get(k, {}),
op,
default=default,
depth=depth + 1,
max_depth=max_depth,
)
else:
types = [type(d[k]) for d in (left, right) if k in d]
msg = (View on GitHub (pinned to e32fa9a52e)
Solutions
- Flatten or prune the nested keys you accumulate — usage dicts should stay shallow (input_tokens/output_tokens/total_tokens plus modest extras).
- If calling `_dict_int_op` directly and depth is legitimate, raise `max_depth` explicitly.
- Audit custom `on_llm_new_token`/usage aggregation code that nests previous results inside new results each step (the usual runaway cause).
- Break reference cycles before combining (deep-copy or rebuild) so depth cannot grow unbounded.
Example fix
# before
def merge_usage(acc, new):
return {"step": acc, "usage": new} # nesting grows every call -> depth blowup
total = _dict_int_op(merge_usage(total, chunk), operator.add)
# after
def merge_usage(acc, new):
return _dict_int_op(acc, new, operator.add) # keep the dict flat
total = merge_usage(total, chunk) Defensive patterns
Strategy: try-catch
Validate before calling
def usage_depth(d: dict, depth: int = 0) -> int:
return max(
[usage_depth(v, depth + 1) for v in d.values() if isinstance(v, dict)] + [depth]
)
if usage_depth(usage_dict) >= 100:
raise ValueError("usage dict nesting too deep; flatten before accumulating") Try / catch
try:
total = _dict_int_op(left, right, operator.add)
except ValueError:
total = left # or recompute from flattened counters Prevention
- Accumulate usage into a flat dict; never nest previous results inside new ones.
- Unit-test usage aggregation over long streaming runs for bounded depth.
- Treat growing depth as a bug in your accumulation loop, not something to raise max_depth for.
When it happens
Trigger: Merging/summing usage metadata dicts (`_dict_int_op(left, right, operator.add)` as done when aggregating token usage across streamed chunks or nested runs) where nesting depth of the value dicts reaches `max_depth`, or calling it directly with a very deeply nested (or cyclic, via shared sub-objects) dict.
Common situations: Custom `UsageMetadata` extensions that add deeply nested accounting keys; summing usage objects that were built by recursive accumulation without depth control; pathological payloads nested by upstream model providers; passing a too-small `max_depth` when calling the helper directly.
Related errors
- Unknown value types: {types}. Only dict and int values are s
- Invalid token_counter shortcut '{token_counter}'. Available
- 'token_counter' expected to be a model that implements 'get_
- Recursion limit reached when invoking {self} with input {inp
- Recursion limit reached when invoking {self} with input {val
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/fa5b0729b88d381e.
Report an issue: GitHub.