{"record":{"id":"08e93e05148d9c74","repo":"OpenBMB/ChatDev","slug":"message-payload-must-be-a-list","errorCode":null,"errorMessage":"message payload must be a list","messagePattern":"message payload must be a list","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"entity/messages.py","lineNumber":410,"sourceCode":"            metadata=data.get(\"metadata\") or {},\n            tool_calls=tool_calls,\n            keep=bool(data.get(\"keep\", False)),\n            preserve_role=bool(data.get(\"preserve_role\", False)),\n        )\n\n\ndef serialize_messages(messages: List[Message], *, include_data: bool = True) -> str:\n    \"\"\"Serialize message list into JSON string.\"\"\"\n    return json.dumps([msg.to_dict(include_data=include_data) for msg in messages], ensure_ascii=False)\n\n\ndef deserialize_messages(payload: str) -> List[Message]:\n    \"\"\"Deserialize JSON string back to messages.\"\"\"\n    if not payload:\n        return []\n    raw = json.loads(payload)\n    if not isinstance(raw, list):\n        raise ValueError(\"message payload must be a list\")\n    return [Message.from_dict(item) for item in raw if isinstance(item, dict)]\n\n\ndef _copy_content(content: MessageContent) -> MessageContent:\n    if content is None:\n        return None\n    if isinstance(content, str):\n        return content\n    copied: List[Any] = []\n    for block in content:\n        if isinstance(block, MessageBlock):\n            copied.append(block.copy())\n        else:\n            copied.append(copy.deepcopy(block))\n    return copied\n","sourceCodeStart":392,"sourceCodeEnd":426,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/messages.py#L392-L426","documentation":"deserialize_messages expects the JSON string payload to decode to a top-level JSON array of message objects. If json.loads yields a dict (single message or {\"messages\": [...]}), a string, or a number, ValueError is raised.","triggerScenarios":"deserialize_messages(json.dumps(single_message_dict)); passing a payload like \"{\\\"messages\\\": [...]}\" produced by another serializer; double-encoded JSON strings.","commonSituations":"Round-trip mismatch: one code path stores serialize_messages' list output, another stores a bare message; API responses wrapping messages in an envelope.","solutions":["Ensure the payload is a JSON array: json.dumps(list_of_message_dicts)","If you have a single message, wrap it: json.dumps([msg_dict])","Unwrap envelopes first: payload = resp[\"messages\"] then serialize/deserialize consistently"],"exampleFix":"# before\ndeserialize_messages(json.dumps(msg_dict))\n# after\ndeserialize_messages(json.dumps([msg_dict]))","handlingStrategy":"type-guard","validationCode":"import json\nraw = json.loads(payload)\nif isinstance(raw, dict):\n    payload = json.dumps(raw.get(\"messages\", [raw]))","typeGuard":"def is_message_list_payload(payload: str) -> bool:\n    try:\n        return isinstance(json.loads(payload), list)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    msgs = deserialize_messages(payload)\nexcept ValueError:\n    raw = json.loads(payload)\n    msgs = [Message.from_dict(m) for m in (raw[\"messages\"] if isinstance(raw, dict) else [raw])]","preventionTips":["Standardize on serialize_messages output (a JSON array)","Never wrap stored payloads in envelopes"],"tags":["messages","json","serialization","validation"],"backgroundTag":"unexpected-json-shape","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}