{"record":{"id":"a11fe007a7036703","repo":"BerriAI/litellm","slug":"message-i-must-have-a-role-field-a11fe0","errorCode":null,"errorMessage":"Message {i} must have a 'role' field","messagePattern":"Message (.+?) must have a 'role' field","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/bedrock/count_tokens/transformation.py","lineNumber":274,"sourceCode":"            raise ValueError(\"model parameter is required\")\n\n        input_type: Final = self._detect_input_type(request_data)\n\n        if input_type == \"converse\":\n            # Validate Converse format (messages-based)\n            messages: Final = request_data.get(\"messages\", [])\n            if not messages:\n                raise ValueError(\"messages parameter is required for Converse input\")\n\n            if not isinstance(messages, list):\n                raise ValueError(\"messages must be a list\")\n\n            for i, message in enumerate(messages):\n                if not isinstance(message, dict):\n                    raise ValueError(f\"Message {i} must be a dictionary\")\n\n                if \"role\" not in message:\n                    raise ValueError(f\"Message {i} must have a 'role' field\")\n\n                if \"content\" not in message:\n                    raise ValueError(f\"Message {i} must have a 'content' field\")\n        else:\n            # For InvokeModel format, we need at least some content to count tokens\n            # The content structure varies by model, so we do minimal validation\n            if len(request_data) <= 1:  # Only has 'model' field\n                raise ValueError(\"Request must contain content to count tokens\")\n","sourceCodeStart":256,"sourceCodeEnd":283,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/bedrock/count_tokens/transformation.py#L256-L283","documentation":"Raised when message i in a Converse-format count-tokens request is a dict but lacks the required 'role' key. Converse messages must carry both 'role' (e.g. 'user'/'assistant') and 'content'; validation reports the exact index so the malformed message is easy to locate.","triggerScenarios":"messages=[{'content': 'hello'}] (content-only dicts); using 'sender' or 'author' instead of 'role'; message dicts built by dict-merge where the role key was dropped.","commonSituations":"Translating from another provider's message format that names the field differently; generic {'text': ...} payloads from internal tooling; partial dict copies ({**m for m in ...} losing keys via filters).","solutions":["Add the role to message i: {'role': 'user', 'content': 'hello'}","If porting from OpenAI-style bodies, rename sender/author fields to 'role'","Validate with a small helper before sending (see defense snippet) so all messages are checked at once"],"exampleFix":"# before\nmessages = [{'content': 'hello'}]\n\n# after\nmessages = [{'role': 'user', 'content': 'hello'}]","handlingStrategy":"type-guard","validationCode":"def assert_roles_present(messages: list[dict]) -> None:\n    for i, m in enumerate(messages):\n        if 'role' not in m:\n            raise ValueError(f'message {i} missing role')\n        if m['role'] not in ('user', 'assistant'):\n            raise ValueError(f\"message {i} role {m['role']!r} not user/assistant\")","typeGuard":"def messages_have_roles(messages: list) -> bool:\n    return all(isinstance(m, dict) and 'role' in m for m in messages)","tryCatchPattern":"try:\n    handler.count_tokens(body)\nexcept ValueError as e:\n    m = re.search(r\"Message (\\d+) must have a 'role' field\", str(e))\n    if m:\n        i = int(m.group(1))\n        body['messages'][i]['role'] = 'user'  # or derive from context\n        handler.count_tokens(body)\n    else:\n        raise","preventionTips":["Rename sender/author to role in your adapter layer","Use a Message TypedDict so missing keys fail static checks","Validate the whole list once before sending instead of fixing index-by-index"],"tags":["bedrock","count-tokens","validation","messages","missing-field"],"backgroundTag":"schema-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}