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
- Simplify the schema: split the tool into smaller ones or hand-write a flattened schema without $defs.
- Remove recursive/self-referential $ref patterns from the schema (they multiply inlined bytes).
- Raise max_inlined_bytes if you legitimately need a large schema and accept the memory cost.
- 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
- Avoid recursive $ref patterns in tool schemas passed to $ref-less providers.
- Keep tool schemas minimal; don't feed entire OpenAPI specs as tool definitions.
- Measure your schema size (json.dumps length) in CI when tools are generated from pydantic models.
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
- CodeInterpreterInterception: no sandbox available. Provide a
- BedrockException - {error_str} . Enable 'litellm.modify_para
- litellm.JSONSchemaValidationError: model={model}, returned a
- tools or tool_choice cannot be set if using text
- Missing required parameter: parameters
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/943b6d8e6e8bbec4.
Report an issue: GitHub.