sgl-project/sglang · error · ValueError

browser.search requires a query

Error message

browser.search requires a query

What it means

SGLang's browser tool dispatch rejects a browser.search action whose parsed JSON arguments contain no truthy 'query' field. The search path requires at least a query string before it can call the underlying Exa search client.

Source

Thrown at python/sglang/srt/entrypoints/tool.py:72

        try:
            args = orjson.loads(last_msg.content[0].text)
            result_text = await self._dispatch_browser_call(context, recipient, args)
        except Exception as exc:
            logger.exception("Browser tool call failed")
            result_text = f"Browser tool call failed: {exc}"

        content = TextContent(text=result_text)
        author = Author(role=Role.TOOL, name=recipient)
        return [Message(author=author, content=[content], recipient=Role.ASSISTANT)]

    async def _dispatch_browser_call(
        self, context: "ConversationContext", recipient: str, args: dict[str, Any]
    ) -> str:
        if recipient == "browser.search":
            query = args.get("query")
            if not query:
                raise ValueError("browser.search requires a query")
            data = await self.exa_client.search(query)
            return self._format_search_results(context, query, data)

        if recipient == "browser.open":
            url = self._resolve_url(context, args)
            data = await self.exa_client.contents([url])
            return self._format_page_contents(context, url, data)

        if recipient == "browser.find":
            pattern = args.get("pattern")
            if not pattern:
                raise ValueError("browser.find requires a pattern")
            return await self._find_pattern(context, args, pattern)

        raise ValueError(f"Unknown browser action: {recipient}")

    def _browser_state(self, context: "ConversationContext") -> dict[str, Any]:
        state = getattr(context, "_sglang_exa_browser_state", None)

View on GitHub (pinned to 0132848349)

Solutions

  1. Ensure the tool-call JSON includes a non-empty "query" string
  2. Validate args before dispatch and re-prompt the model to correct empty arguments
  3. Align the tool schema advertised to the model with {"query": string, required}

Example fix

# before
args = {"q": "sglang"}
# after
args = {"query": "sglang"}
Defensive patterns

Strategy: validation

Validate before calling

args = orjson.loads(last_msg.content[0].text)
if not args.get("query"):
    raise ValueError("browser.search requires a query")  # fail fast client-side

Type guard

def valid_search_args(args: dict) -> bool:
    return isinstance(args.get("query"), str) and bool(args["query"].strip())

Try / catch

try:
    out = await tool._dispatch_browser_call(context, "browser.search", args)
except ValueError as e:
    if "requires a query" in str(e):
        args["query"] = fallback_query  # or re-prompt the model
        out = await tool._dispatch_browser_call(context, "browser.search", args)

Prevention

When it happens

Trigger: Dispatching recipient 'browser.search' with args like {}, {"query": ""}, or {"query": null} after orjson parsing of the message content.

Common situations: A model emitting malformed/empty tool arguments (hallucinated schema), a client constructing browser.search calls manually without a query, or JSON schema drift where the field was renamed (e.g. q instead of query).

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/667439662361f867. Report an issue: GitHub.