BerriAI/litellm · error · ValueError

Unsupported content type: {type(content_block)}

Error message

Unsupported content type: {type(content_block)}

What it means

Raised while normalizing a user message's content for empty-text replacement (the function that swaps empty text blocks for a 'continue' message). It explicitly handles content being a str or a list of blocks; any other Python type — None, dict, int — hits the final ValueError.

Source

Thrown at litellm/litellm_core_utils/prompt_templates/factory.py:4130

        modified_content_block: Final = content_block.copy()

        for item in modified_content_block:
            # Check if the list is empty
            if item["type"] == "text":
                if not item["text"].strip():
                    # Replace empty text with continue message
                    _user_continue_message = ChatCompletionUserMessage(
                        **(user_continue_message or DEFAULT_USER_CONTINUE_MESSAGE)
                    )
                    text = convert_content_list_to_str(_user_continue_message)
                    item["text"] = text
                    break
        modified_message: Final = message.copy()
        modified_message["content"] = modified_content_block
        return modified_message

    # Handle unsupported type
    raise ValueError(f"Unsupported content type: {type(content_block)}")


def return_assistant_continue_message(
    assistant_continue_message: str | ChatCompletionAssistantMessage | None = None,
) -> ChatCompletionAssistantMessage:
    if assistant_continue_message and isinstance(assistant_continue_message, str):
        return ChatCompletionAssistantMessage(
            role="assistant",
            content=assistant_continue_message,
        )
    elif assistant_continue_message and isinstance(assistant_continue_message, dict):
        return ChatCompletionAssistantMessage(**assistant_continue_message)
    else:
        return DEFAULT_ASSISTANT_CONTINUE_MESSAGE


def _skip_empty_dict_blocks(blocks: list[dict]) -> list[dict]:
    """

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Set user message content to a non-empty string, or to a list of typed blocks.
  2. If content is None because of tool results, supply content: "" is not enough — use an explicit text block or omit the user message and use the tool role expected by the handler.
  3. Wrap single blocks in a list: content=[{"type":"text","text":"..."}].

Example fix

# before
{"role": "user", "content": None}
# after
{"role": "user", "content": [{"type": "text", "text": " "}]}
Defensive patterns

Strategy: type-guard

Validate before calling

def sanitize_content(msg: dict) -> dict:
    c = msg.get("content")
    if c is None:
        msg = {**msg, "content": [{"type": "text", "text": " "}]}
    elif isinstance(c, dict):
        msg = {**msg, "content": [c]}
    return msg

Type guard

def content_is_supported(c) -> bool:
    return isinstance(c, (str, list))

Try / catch

try:
    resp = litellm.completion(model="bedrock/...", messages=msgs)
except ValueError as e:
    if "Unsupported content type" in str(e):
        msgs = [sanitize_content(m) for m in msgs]
        resp = litellm.completion(model="bedrock/...", messages=msgs)

Prevention

When it happens

Trigger: A message whose 'content' key is None (common when a user message has tool results but content omitted), a single content-block dict instead of a list, or any non-str/non-list value, sent to a Bedrock Converse model.

Common situations: Messages built for OpenAI tool flows where content is optional; agent frameworks that emit content=None for tool-result turns; passing one content block as a bare dict {"type":"text",...} instead of wrapping it in a list.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/701e2915258f91d9. Report an issue: GitHub.