OpenBMB/ChatDev · error · ValueError

message dict missing role

Error message

message dict missing role

What it means

Message.from_dict requires a truthy 'role' key; a dict without role (or with role: null/"") raises ValueError. The role is then fed to MessageRole(role_value), so an unknown role string raises ValueError from the enum instead.

Source

Thrown at entity/messages.py:350

        if self.name:
            payload["name"] = self.name
        if self.tool_call_id:
            payload["tool_call_id"] = self.tool_call_id
        if self.metadata:
            payload["metadata"] = self.metadata
        if self.tool_calls:
            payload["tool_calls"] = [call.to_openai_dict() for call in self.tool_calls]
        if self.keep:
            payload["keep"] = self.keep
        if self.preserve_role:
            payload["preserve_role"] = self.preserve_role
        return payload

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Message":
        role_value = data.get("role")
        if not role_value:
            raise ValueError("message dict missing role")
        role = MessageRole(role_value)
        content = data.get("content")
        if isinstance(content, list):
            converted: List[MessageBlock] = []
            for block in content:
                if isinstance(block, MessageBlock):
                    converted.append(block)
                elif isinstance(block, dict):
                    try:
                        converted.append(MessageBlock.from_dict(block))
                    except Exception:
                        # Preserve raw dict for debugging; text_content will stringify best-effort
                        converted.append(
                            MessageBlock(
                                type=MessageBlockType.DATA,
                                text=str(block),
                                data=block,
                            )

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Include a valid role key: "role": "user" (or assistant/system/tool per MessageRole)
  2. If the dict may be incomplete, guard with item.get("role") checks before calling from_dict
  3. Regenerate/re-fetch the source JSON if a writer dropped the field

Example fix

// before
Message.from_dict({"content": "hi"})
// after
Message.from_dict({"role": "user", "content": "hi"})
Defensive patterns

Strategy: type-guard

Validate before calling

if not item.get("role"):
    item["role"] = "user"  # or skip/reject the record

Type guard

def is_valid_message_dict(d) -> bool:
    roles = {"system", "user", "assistant", "tool"}
    return isinstance(d, dict) and d.get("role") in roles

Try / catch

try:
    msg = Message.from_dict(item)
except ValueError:
    log.warning("dropping malformed message: %r", item)

Prevention

When it happens

Trigger: Message.from_dict({"content": "hi"}); deserializing partial/stripped JSON where role was dropped; role: "" from template rendering.

Common situations: Persisted message JSON mutated downstream; LLM tool output misshapoen dicts; hand-building dicts for tests and forgetting role.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27). Data as JSON: /api/errors/31d96c6b1ff5df9a. Report an issue: GitHub.