sgl-project/sglang · error · ValueError
tool_choice={tc_type!r} requires at least one custom tool; a
Error message
tool_choice={tc_type!r} requires at least one custom tool; all supplied tools were server-side Anthropic built-ins which the OpenAI-compatible backend cannot invoke What it means
Raised when tool_choice is 'any' or 'tool' but the request contains no custom (client-defined) tools — only server-side Anthropic built-in tools, which SGLang's OpenAI-compatible backend cannot invoke. Forcing tool use therefore has nothing to force, so the request is rejected with a 400.
Source
Thrown at python/sglang/srt/entrypoints/anthropic/serving.py:724
tool_name = anthropic_request.tool_choice.name
# ``Tool.function`` is a ``Function`` Pydantic model, not
# a dict — access by attribute. A dict ``.get`` would
# AttributeError and surface as a 500 instead of the
# intended 400 / happy path.
if not any(
t.function.name == tool_name for t in chat_request.tools
):
raise ValueError(
f"tool_choice references tool {tool_name!r} but it "
f"is not in the forwarded tools list "
f"(server-side Anthropic tools cannot be selected)"
)
chat_request.tool_choice = ToolChoice(
type="function",
function=ToolChoiceFuncName(name=tool_name),
)
elif tc_type in ("any", "tool"):
raise ValueError(
f"tool_choice={tc_type!r} requires at least one custom "
f"tool; all supplied tools were server-side Anthropic "
f"built-ins which the OpenAI-compatible backend cannot "
f"invoke"
)
elif chat_request.tools:
chat_request.tool_choice = "auto"
return chat_request
async def _handle_non_streaming(
self,
chat_request: ChatCompletionRequest,
anthropic_request: AnthropicMessagesRequest,
raw_request: Request,
) -> JSONResponse:
"""Handle non-streaming Anthropic request by delegating to OpenAI handler."""
# ``monotonic_time`` is ``time.perf_counter`` under the hood; theView on GitHub (pinned to 0132848349)
Solutions
- Add at least one custom tool definition ({"type": "custom", "name": ..., "input_schema": ...}) if you need tool_choice 'any'/'tool'
- Or change tool_choice to 'auto' when only built-in tools are supplied
- Note built-in tools are forwarded but not executed by the backend; implement tools client-side
Example fix
// before
{"tools": [{"type": "web_search_20250305", "name": "web_search"}], "tool_choice": {"type": "any"}}
// after
{"tools": [{"type": "web_search_20250305", "name": "web_search"}], "tool_choice": {"type": "auto"}} Defensive patterns
Strategy: validation
Validate before calling
has_custom = any(t.get("type") in ("custom", "function") for t in tools)
if tool_choice.get("type") in ("any", "tool") and not has_custom:
tool_choice = {"type": "auto"} Type guard
def can_force_tool(tool_choice, tools) -> bool:
return tool_choice.get("type") not in ("any", "tool") or any(
t.get("type") in ("custom", "function") for t in tools) Prevention
- Only force tool use when you define custom tools
- Fall back to 'auto' when tools are all built-ins
When it happens
Trigger: POST /v1/messages with tool_choice={"type": "any"} and tools containing only Anthropic built-ins like {"type": "web_search_20250305", "name": "web_search"}.
Common situations: Copy-pasting Anthropic cookbook examples that combine built-in tools with forced tool_choice; assuming SGLang proxies Anthropic server-side tool execution (it doesn't — those tools aren't executed locally).
Related errors
- tool_choice references tool {tool_name!r} but it is not in t
- Anthropic redacted_thinking history is not supported
- thinking.budget_tokens must be >= 1024 (got {})
- thinking.budget_tokens is not allowed when thinking.type is
- thinking.display is not allowed when thinking.type is 'disab
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/deb368deb19e0bd1.
Report an issue: GitHub.