sgl-project/sglang · error · ValueError

The system message should be a single text.

Error message

The system message should be a single text.

What it means

A system message whose content is a list must contain exactly one part of type 'text'. If the list is empty, has more than one element, or its first element's type is not 'text' (e.g. 'image_url'), generate_chat_conv raises this ValueError.

Source

Thrown at python/sglang/srt/parser/conversation.py:640

        image_token=conv.image_token,
        audio_token=conv.audio_token,
        video_token=conv.video_token,
        image_token_at_prefix=conv.image_token_at_prefix,
    )

    if isinstance(request.messages, str):
        raise ValueError("The messages should be a list of dict.")
    for message in request.messages:
        msg_role = message.role
        if msg_role == "system":
            if isinstance(message.content, str):
                conv.system_message = message.content
            elif isinstance(message.content, list):
                if (
                    len(message.content) != 1
                    or getattr(message.content[0], "type", None) != "text"
                ):
                    raise ValueError("The system message should be a single text.")
                else:
                    conv.system_message = getattr(message.content[0], "text", "")
        elif msg_role == "user":
            # Handle the various types of Chat Request content types here.
            if isinstance(message.content, str):
                conv.append_message(conv.roles[0], message.content)
            else:
                real_content = ""
                # calculate number of image_url
                num_image_url = 0
                for content in message.content:
                    if content.type == "image_url":
                        num_image_url += 1
                        conv.modalities.append(content.modalities)
                image_token = (
                    conv.image_token + "\n"
                    if conv.name not in ("qwen2-vl", "moss-vl", "unlimited-ocr")
                    else conv.image_token

View on GitHub (pinned to 0132848349)

Solutions

  1. Make the system message content a plain string: "content": "You are helpful"
  2. If using a list, keep exactly one part with "type":"text"
  3. Move images to a user message — system role cannot carry multimodal content

Example fix

# before
{"role":"system","content":[{"type":"text","text":"a"},{"type":"text","text":"b"}]}
# after
{"role":"system","content":"You are helpful"}
Defensive patterns

Strategy: type-guard

Validate before calling

for m in messages:
    if m["role"] == "system" and isinstance(m["content"], list):
        assert len(m["content"]) == 1 and m["content"][0].get("type") == "text"

Type guard

def ok_system(c): return isinstance(c, str) or (isinstance(c, list) and len(c)==1 and isinstance(c[0], dict) and c[0].get("type")=="text")

Prevention

When it happens

Trigger: Sending messages=[{"role":"system","content":[{"type":"image_url",...}]}, ...] or a system message with content=[{"type":"text",...},{"type":"text",...}] (two parts).

Common situations: Copying multimodal user-message formatting into the system role; frontends that always wrap content in a parts list.

Related errors


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