agentscope-ai/agentscope · error · ValueError

System message can only contain text blocks.

Error message

System message can only contain text blocks.

What it means

System-role messages are restricted to TextBlock only; any other block type (data, image, tool results) in a system message raises this ValueError during validate_role_content. System prompts are plain text by contract across providers.

Source

Thrown at src/agentscope/message/_base.py:48

    AgentEvent = Any


def _assert_user_content_blocks(content: Sequence[ContentBlock]) -> None:
    """Assert that the content blocks in user message are valid."""
    for block in content:
        if block.type not in ["text", "data"]:
            raise ValueError(
                "User message can only contain text blocks or data blocks.",
            )


def _assert_system_content_blocks(
    content: Sequence[ContentBlock],
) -> None:
    """Assert that the content blocks in system message are valid."""
    for block in content:
        if block.type not in ["text"]:
            raise ValueError("System message can only contain text blocks.")


def _to_blocks(content: str | list) -> list:
    """Convert a plain string to a single-element TextBlock list."""
    if isinstance(content, str):
        return [TextBlock(text=content)]
    return content


class Usage(BaseModel):
    """The token usage information of a message."""

    input_tokens: int
    """The number of input tokens."""
    output_tokens: int
    """The number of output tokens."""
    cache_input_tokens: int = 0
    """The number of input tokens read from the prompt cache."""

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Keep system messages as a single TextBlock (or a plain string)
  2. Move data blocks to the first user message or a tool result
  3. Validate block types per role before constructing Msg objects

Example fix

// before
msg = Msg("system", role="system", content=[
    TextBlock(type="text", text="You are helpful."),
    DataBlock(type="data", data={"kb": "..."}),
])

// after
msg = Msg("system", role="system", content="You are helpful.")
first_user = Msg("user", role="user", content=[
    TextBlock(type="text", text="Context:"),
    DataBlock(type="data", data={"kb": "..."}),
])
Defensive patterns

Strategy: type-guard

Validate before calling

assert all(b.type == "text" for b in system_blocks), "system prompt must be text-only"

Type guard

def is_valid_system_content(blocks) -> bool:
    return all(b.type == "text" for b in blocks)

Try / catch

try:
    Msg("system", role="system", content=blocks)
except ValueError as e:
    if "only contain text blocks" in str(e):
        blocks = [b for b in blocks if b.type == "text"]

Prevention

When it happens

Trigger: Msg("system", role="system", content=[TextBlock(...), DataBlock(...)]) or embedding images/structured data in the system prompt.

Common situations: Programmatically building all messages from a uniform block pipeline that injects data blocks into every role; migrating configs where system prompts were templated with structured payloads; concatenating a data block meant for the first user turn.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/9ecc85a23ae14909. Report an issue: GitHub.