BerriAI/litellm · error · ValueError

unpack_defs: inlined schema exceeded the {max_inlined_bytes:

Error message

unpack_defs: inlined schema exceeded the {max_inlined_bytes:,}-byte budget. Refusing to deep-copy further to prevent schema-bomb resource exhaustion.

What it means

unpack_defs inlines $ref targets from a JSON schema's $defs/definitions into the main schema (needed by providers that don't support $ref). To prevent schema-bomb resource exhaustion, it accumulates the estimated byte size of every inlined target and aborts with ValueError once a configurable budget (max_inlined_bytes) is exceeded, rather than deep-copying unbounded recursive structures.

Source

Thrown at litellm/litellm_core_utils/prompt_templates/common_utils.py:943

        if isinstance(node, dict):
            # --- Case 1: this node *is* a reference ---
            if "$ref" in node:
                ref_name = node["$ref"].split("/")[-1]

                # Check for circular reference in the resolution chain
                if ref_name in ref_chain:
                    # Circular reference detected - leave as-is to prevent infinite recursion
                    continue

                target_schema = active_defs.get(ref_name)
                # Unknown reference – leave untouched
                if target_schema is None:
                    continue

                if max_inlined_bytes is not None:
                    inlined_bytes += _estimate_json_bytes(target_schema)
                    if inlined_bytes > max_inlined_bytes:
                        raise ValueError(
                            f"unpack_defs: inlined schema exceeded the "
                            f"{max_inlined_bytes:,}-byte budget. Refusing to "
                            f"deep-copy further to prevent schema-bomb "
                            f"resource exhaustion."
                        )

                # Merge defs from the target to capture nested definitions
                child_defs = {
                    **active_defs,
                    **target_schema.get("$defs", {}),
                    **target_schema.get("definitions", {}),
                }

                # Replace the reference with resolved copy
                resolved = copy.deepcopy(target_schema)
                if parent is not None and key is not None:
                    if (
                        isinstance(parent, dict)

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Simplify the schema: split the tool into smaller ones or hand-write a flattened schema without $defs.
  2. Remove recursive/self-referential $ref patterns from the schema (they multiply inlined bytes).
  3. Raise max_inlined_bytes if you legitimately need a large schema and accept the memory cost.
  4. Use providers that natively support $ref so inlining is unnecessary.

Example fix

// before
tools=[{'type':'function','function':{'name':'db','parameters': giant_openapi_schema_with_defs}}]

# after
tools=[{'type':'function','function':{'name':'get_user','parameters': {'type':'object','properties':{'id':{'type':'string'}},'required':['id']}}}]
Defensive patterns

Strategy: validation

Validate before calling

import json

def schema_within_budget(schema: dict, max_bytes: int = 1_000_000) -> bool:
    def estimate(o):
        if isinstance(o, dict): return sum(estimate(k) + estimate(v) for k, v in o.items())
        if isinstance(o, list): return sum(estimate(i) for i in o)
        return len(json.dumps(o))
    # rough: inlined defs cost ~2x original schema when refs repeat
    return estimate(schema) * 2 <= max_bytes

Try / catch

try:
    resp = litellm.completion(model=m, messages=msgs, tools=tools)
except ValueError as e:
    if 'unpack_defs' in str(e):
        tools = flatten_schema_manually(tools)  # pre-inline only needed defs, drop the rest
        resp = litellm.completion(model=m, messages=msgs, tools=tools)
    else:
        raise

Prevention

When it happens

Trigger: Passing tools/response_format with huge or self-referentially expanding $defs (e.g. a recursive schema whose $ref targets pull in the entire defs tree each time); many mutually-referencing definitions that blow past the byte budget when inlined; providers that require full inlining (no $ref support) combined with large OpenAPI-generated schemas.

Common situations: Auto-generated pydantic/jsonschema models with deeply nested $defs; passing an entire database schema or OpenAPI spec as a tool definition; a low max_inlined_bytes configured by the caller or by a security-conscious default.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/943b6d8e6e8bbec4. Report an issue: GitHub.