OpenBMB/ChatDev · error · ConfigError
role must be 'user' or 'assistant'
Error message
role must be 'user' or 'assistant'
What it means
LiteralNodeConfig.from_dict rejects an optional 'role' that is not one of the two chat roles 'user' or 'assistant' (compared case-insensitively after strip). Omitting 'role' defaults to USER; only bad explicit values fail.
Source
Thrown at entity/configs/node/literal.py:37
class LiteralNodeConfig(BaseConfig):
"""Config describing the literal payload emitted by the node."""
content: str = ""
role: MessageRole = MessageRole.USER
@classmethod
def from_dict(cls, data: Mapping[str, Any], *, path: str) -> "LiteralNodeConfig":
mapping = require_mapping(data, path)
content = require_str(mapping, "content", path)
if not content:
raise ConfigError("content cannot be empty", f"{path}.content")
role_value = optional_str(mapping, "role", path)
role = MessageRole.USER
if role_value:
normalized = role_value.strip().lower()
if normalized not in (MessageRole.USER.value, MessageRole.ASSISTANT.value):
raise ConfigError("role must be 'user' or 'assistant'", f"{path}.role")
role = MessageRole(normalized)
return cls(content=content, role=role, path=path)
def validate(self) -> None:
if not self.content:
raise ConfigError("content cannot be empty", f"{self.path}.content")
if self.role not in (MessageRole.USER, MessageRole.ASSISTANT):
raise ConfigError("role must be 'user' or 'assistant'", f"{self.path}.role")
FIELD_SPECS = {
"content": ConfigFieldSpec(
name="content",
display_name="Literal Content",
type_hint="text",
required=True,
description="Plain text emitted whenever the node executes.",
),View on GitHub (pinned to 4fb2db0ea9)
Solutions
- Use only 'user' or 'assistant' for role
- Omit 'role' to get the default 'user'
- Filter/remap system messages before constructing literal node configs
Example fix
# before
{"content": "hi", "role": "system"}
# after
{"content": "hi", "role": "user"} Defensive patterns
Strategy: type-guard
Validate before calling
LITERAL_ROLES = {'user', 'assistant'}
role = (data.get('role') or 'user').strip().lower()
if role not in LITERAL_ROLES:
data['role'] = 'user' Type guard
def valid_literal_role(data: dict) -> bool:
r = data.get('role')
return r is None or (isinstance(r, str) and r.strip().lower() in {'user', 'assistant'}) Try / catch
try:
LiteralNodeConfig.from_dict(data, path='n1')
except ConfigError as e:
if e.path.endswith('role'):
data['role'] = 'user'
LiteralNodeConfig.from_dict(data, path='n1')
else:
raise Prevention
- Map 'system'/'tool' roles to 'user' before building literal nodes
- Restrict role pickers to user/assistant
- Lowercase and strip role strings from external payloads
When it happens
Trigger: Passing role values like 'system', 'tool', 'bot', 'model', or typos like 'asistant'. Matching is lenient about case/whitespace but not about the value itself.
Common situations: Porting message lists from OpenAI-style payloads that include 'system' or 'tool' roles into literal nodes; UI dropdowns exposing unsupported roles.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- model.input_mode must be 'prompt' or 'messages'
- content cannot be empty
- duration_unit must be one of: {', '.join(valid_units)}
- model.name must be a non-empty string
- tooling must be a list
AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27).
Data as JSON: /api/errors/83c83e224980012e.
Report an issue: GitHub.