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

  1. Flatten or prune the nested keys you accumulate — usage dicts should stay shallow (input_tokens/output_tokens/total_tokens plus modest extras).
  2. If calling `_dict_int_op` directly and depth is legitimate, raise `max_depth` explicitly.
  3. Audit custom `on_llm_new_token`/usage aggregation code that nests previous results inside new results each step (the usual runaway cause).
  4. 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

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


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/fa5b0729b88d381e. Report an issue: GitHub.