sgl-project/sglang · error · ValueError

Invalid number of contents in browser message

Error message

Invalid number of contents in browser message

What it means

For harmony messages addressed to a 'browser.*' recipient, parse_output_message expects exactly one content part (the JSON-encoded browser call). If message.content has any length other than 1, the structure is ambiguous and parsing aborts.

Source

Thrown at python/sglang/srt/entrypoints/harmony_utils.py:258

    return get_encoding().stop_tokens_for_assistant_actions()


def get_streamable_parser_for_assistant() -> StreamableParser:
    return StreamableParser(get_encoding(), role=Role.ASSISTANT)


def parse_output_message(message: Message):
    if message.author.role != "assistant":
        # This is a message from a tool to the assistant (e.g., search result).
        # Don't include it in the final output for now. This aligns with
        # OpenAI's behavior on models like o4-mini.
        return []

    output_items = []
    recipient = message.recipient
    if recipient is not None and recipient.startswith("browser."):
        if len(message.content) != 1:
            raise ValueError("Invalid number of contents in browser message")
        content = message.content[0]
        browser_call = orjson.loads(content.text)
        # TODO: translate to url properly!
        if recipient == "browser.search":
            action = ActionSearch(
                query=f"cursor:{browser_call.get('query', '')}", type="search"
            )
        elif recipient == "browser.open":
            action = ActionOpenPage(
                url=f"cursor:{browser_call.get('url', '')}", type="open_page"
            )
        elif recipient == "browser.find":
            action = ActionFind(
                pattern=browser_call["pattern"],
                url=f"cursor:{browser_call.get('url', '')}",
                type="find",
            )
        else:

View on GitHub (pinned to 0132848349)

Solutions

  1. Ensure browser.* messages contain exactly one content part holding the full JSON payload
  2. If post-processing model output, merge split content parts into one before parsing
  3. Upgrade sglang if a newer version tolerates/normalizes multi-part browser messages

Example fix

# before
msg = Message(..., recipient="browser.search", content=[p1, p2])
# after
msg = Message(..., recipient="browser.search", content=[merged_single_part])
Defensive patterns

Strategy: validation

Validate before calling

if message.recipient and message.recipient.startswith("browser."):
    assert len(message.content) == 1, "browser messages need exactly one content part"

Type guard

def is_wellformed_browser_message(message) -> bool:
    return (
        message.recipient is not None
        and message.recipient.startswith("browser.")
        and len(message.content) == 1
    )

Try / catch

try:
    items = parse_output_message(message)
except ValueError as e:
    if "Invalid number of contents" in str(e):
        return []  # drop malformed browser message
    raise

Prevention

When it happens

Trigger: A model-produced or hand-crafted message with recipient like 'browser.search' whose content list is empty or has 2+ parts.

Common situations: Malformed model output splitting the browser-call JSON across multiple content blocks; clients assembling harmony messages manually and appending extra text parts.

Related errors


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