{"record":{"id":"34736f53a762b65d","repo":"BerriAI/litellm","slug":"message-i-must-be-a-dictionary-34736f","errorCode":null,"errorMessage":"Message {i} must be a dictionary","messagePattern":"Message (.+?) must be a dictionary","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/bedrock/count_tokens/transformation.py","lineNumber":271,"sourceCode":"            ValueError: If the request is invalid\n        \"\"\"\n        if not request_data.get(\"model\"):\n            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":253,"sourceCodeEnd":283,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/bedrock/count_tokens/transformation.py#L253-L283","documentation":"Per-message structural validation for Converse-style count-tokens input: every element of 'messages' must be a dict. A string, tuple, or None element at index i raises ValueError naming the offending index.","triggerScenarios":"messages = ['hello world', ...] where plain strings were passed instead of message objects, or mixed lists where one element is a content block rather than a message.","commonSituations":"Porting code from chat APIs that accept plain-string turns, or array spreads that accidentally inline content blocks.","solutions":["Convert every turn to {'role': ..., 'content': ...} dicts","Sanitize: messages = [m if isinstance(m, dict) else {'role': 'user', 'content': [{'text': str(m)}]} for m in messages]"],"exampleFix":"# before\nreq = {'model': m, 'messages': ['hello', 'hi']}\n\n# after\nreq = {'model': m, 'messages': [\n    {'role': 'user', 'content': [{'text': 'hello'}]},\n    {'role': 'assistant', 'content': [{'text': 'hi'}]},\n]}","handlingStrategy":"type-guard","validationCode":"for i, m in enumerate(req.get(\"messages\", [])):\n    if not isinstance(m, dict):\n        raise TypeError(f\"Message {i} must be a dictionary\")","typeGuard":"def is_valid_message(m) -> bool:\n    return isinstance(m, dict) and isinstance(m.get(\"role\"), str) and (\"content\" in m)","tryCatchPattern":null,"preventionTips":["Normalize plain-string turns into message dicts at ingestion","Validate the whole conversation with one helper before any LiteLLM call"],"tags":["bedrock","count-tokens","validation","types"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}