BerriAI/litellm · error · ValueError

defer_loading must be a boolean

Error message

defer_loading must be a boolean

What it means

While mapping optional tool attributes, LiteLLM found a top-level 'defer_loading' key on a tool that supports it (i.e. not a tool-search or computer tool) but its value is not a Python bool. The value is type-checked strictly, so strings like 'true' or 1/0 are rejected with this ValueError.

Source

Thrown at litellm/llms/anthropic/chat/transformation.py:802

                    returned_tool["cache_control"] = _cache_control
                elif _cache_control_function is not None and isinstance(_cache_control_function, dict):
                    returned_tool["cache_control"] = ChatCompletionCachedContent(**_cache_control_function)

        ## check if defer_loading is set in the tool
        _defer_loading: Final = tool.get("defer_loading", None)
        _defer_loading_function: Final = tool.get("function", {}).get("defer_loading", None)
        if returned_tool is not None:
            # Only set defer_loading on tools that support it (not tool search tools or computer tools)
            tool_type = returned_tool.get("type", "")
            if tool_type not in (
                "tool_search_tool_regex_20251119",
                "tool_search_tool_bm25_20251119",
                "computer_20241022",
                "computer_20250124",
            ):
                if _defer_loading is not None:
                    if not isinstance(_defer_loading, bool):
                        raise ValueError("defer_loading must be a boolean")
                    returned_tool["defer_loading"] = _defer_loading
                elif _defer_loading_function is not None:
                    if not isinstance(_defer_loading_function, bool):
                        raise ValueError("defer_loading must be a boolean")
                    returned_tool["defer_loading"] = _defer_loading_function

        ## check if allowed_callers is set in the tool
        _allowed_callers: Final = tool.get("allowed_callers", None)
        _allowed_callers_function: Final = tool.get("function", {}).get("allowed_callers", None)
        if returned_tool is not None:
            # Only set allowed_callers on tools that support it (not tool search tools or computer tools)
            tool_type = returned_tool.get("type", "")
            if tool_type not in (
                "tool_search_tool_regex_20251119",
                "tool_search_tool_bm25_20251119",
                "computer_20241022",
                "computer_20250124",
            ):

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Use a real boolean: defer_loading: true (unquoted) in YAML, or true in JSON.
  2. If the value comes from config/env, coerce before building the tool: bool parsing like value in ('true', '1', True).
  3. Note defer_loading may also live under 'function' (that path is checked only when the top-level key is absent).

Example fix

# before (YAML/config)
"defer_loading": "true"  # string -> ValueError

# after
"defer_loading": True    # actual boolean
Defensive patterns

Strategy: validation

Validate before calling

for t in tools:
    if "defer_loading" in t and not isinstance(t["defer_loading"], bool):
        t["defer_loading"] = str(t["defer_loading"]).lower() in ("true", "1")

Type guard

def defer_loading_ok(tool) -> bool:
    v = tool.get("defer_loading")
    return v is None or isinstance(v, bool)

Prevention

When it happens

Trigger: Passing {'type': 'custom', ..., 'defer_loading': 'true'} or defer_loading: 1 in a tool dict; values loaded from YAML/JSON config where booleans arrive as strings ('true'/'false') or ints.

Common situations: YAML configs parsed without bool coercion (only literal true/false become bool; quoted 'true' stays a string); JSON produced by another service that serializes booleans as 0/1; env-var-driven flags injected as strings.

Related errors


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