{"record":{"id":"e862b38b50aa0e60","repo":"BerriAI/litellm","slug":"advisor-tool-definition-must-include-a-model-fie","errorCode":null,"errorMessage":"advisor tool definition must include a 'model' field specifying the advisor model","messagePattern":"advisor tool definition must include a 'model' field specifying the advisor model","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py","lineNumber":80,"sourceCode":"        stream: bool | None,\n        max_tokens: int,\n        custom_llm_provider: str | None,\n        **kwargs,\n    ) -> AnthropicMessagesResponse | AsyncIterator:\n        from litellm.llms.anthropic.experimental_pass_through.messages.fake_stream_iterator import (\n            FakeAnthropicMessagesStreamIterator,\n        )\n\n        # Extract advisor tool config.\n        advisor_tool: Final = next(\n            (t for t in (tools or []) if t.get(\"type\") == ANTHROPIC_ADVISOR_TOOL_TYPE),\n            None,\n        )\n        if advisor_tool is None:\n            raise ValueError(f\"handle() called but no {ANTHROPIC_ADVISOR_TOOL_TYPE} tool found in tools list\")\n        advisor_model: Final[str] = advisor_tool.get(\"model\") or \"\"\n        if not advisor_model:\n            raise ValueError(\"advisor tool definition must include a 'model' field specifying the advisor model\")\n        _raw_max_uses: Final = advisor_tool.get(\"max_uses\")\n        max_uses: Final[int] = ADVISOR_MAX_USES if _raw_max_uses is None else int(_raw_max_uses)\n        advisor_api_key, advisor_api_base = _resolve_advisor_credentials(advisor_tool)\n\n        # Build the synthetic tool definition the provider will receive.\n        synthetic_advisor_tool: Final = _make_synthetic_advisor_tool()\n\n        # Executor tools = all original tools with advisor replaced by the synthetic one.\n        executor_tools: Final[list[dict]] = [\n            (synthetic_advisor_tool if t.get(\"type\") == ANTHROPIC_ADVISOR_TOOL_TYPE else t) for t in (tools or [])\n        ]\n\n        # Strip prior advisor blocks from history, preserving advice text as context.\n        current_messages: list[dict] = strip_advisor_blocks_from_messages(\n            [dict(m) for m in messages], replace_with_text=True\n        )\n\n        parent_request_id: Final[str] = str(kwargs.pop(\"litellm_call_id\", None) or uuid.uuid4())","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/anthropic/experimental_pass_through/messages/interceptors/advisor.py#L62-L98","documentation":"Raised by the advisor interceptor when it finds a tool of type 'advisor' in the tools list but that tool definition has no (or empty) 'model' field. The advisor pattern routes pre-execution advice through a separate advisor model, so the interceptor must know which model to call; the field is mandatory.","triggerScenarios":"Sending a /v1/messages request with tools=[{\"type\": \"advisor\", \"max_uses\": 1}] — any advisor tool dict lacking 'model' or with model=\"\". The interceptor extracts advisor_tool.get('model') and rejects empty strings.","commonSituations":"Enabling the advisor tool based on docs that only mention type/max_uses; refactoring that renames 'model' to 'advisor_model'; conditionally building the tool dict and dropping the model key when a config value is unset.","solutions":["Add a 'model' field to the advisor tool definition naming the model that gives advice, e.g. {\"type\": \"advisor\", \"model\": \"claude-haiku-4-5\", \"max_uses\": 1}.","If the model name comes from config, assert it is non-empty before attaching the advisor tool.","Omit the advisor tool entirely if you do not want advisory behavior."],"exampleFix":"# before\ntools = [{\"type\": \"advisor\", \"max_uses\": 1}]\n\n# after\ntools = [{\"type\": \"advisor\", \"model\": \"claude-haiku-4-5\", \"max_uses\": 1}]","handlingStrategy":"validation","validationCode":"def make_advisor_tool(model: str, max_uses: int | None = None) -> dict:\n    if not model:\n        raise ValueError(\"advisor tool requires a non-empty 'model'\")\n    tool = {\"type\": \"advisor\", \"model\": model}\n    if max_uses is not None:\n        tool[\"max_uses\"] = max_uses\n    return tool","typeGuard":"def is_valid_advisor_tool(tool: object) -> bool:\n    return (\n        isinstance(tool, dict)\n        and tool.get(\"type\") == \"advisor\"\n        and isinstance(tool.get(\"model\"), str)\n        and bool(tool[\"model\"].strip())\n    )","tryCatchPattern":"try:\n    resp = litellm.anthropic_messages(tools=tools, ...)\nexcept ValueError as e:\n    if \"advisor tool definition must include a 'model'\" in str(e):\n        return http_error(400, str(e))\n    raise","preventionTips":["Build advisor tools through a single factory that enforces the model field.","Assert advisor model config is set before attaching the tool per-request.","Skip the advisor tool entirely when no advisor model is configured."],"tags":["anthropic","advisor","tools","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}