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
- Keep system messages as a single TextBlock (or a plain string)
- Move data blocks to the first user message or a tool result
- 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
- Pass system prompts as plain strings
- Route structured context/data blocks into user turns or tool results
- Validate message histories by role before persisting or replaying them
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
- User message can only contain text blocks or data blocks.
- The system prompt {suffix}exceed(s) the compression threshol
- Input must be a list of Msg objects.
- f"{type(self).__name__} does not implement on_system_prompt"
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/9ecc85a23ae14909.
Report an issue: GitHub.