BerriAI/litellm · error · Exception

Unsupported content type: {type(content)}

Error message

Unsupported content type: {type(content)}

What it means

Raised by the Databricks chat transformer when a message's 'content' is neither None, a string, nor a list — the only content shapes it knows how to convert to text. The exception message includes the offending Python type. This happens during request/response content normalization, typically for assistant/tool message content in non-streaming transformations.

Source

Thrown at litellm/llms/databricks/chat/transformation.py:483

        return cast(AllMessageValues, transformed_message)

    @staticmethod
    def extract_content_str(
        content: AllDatabricksContentValues | None,
    ) -> str | None:
        if content is None:
            return None
        if isinstance(content, str):
            return content
        elif isinstance(content, list):
            content_str = ""
            for item in content:
                if item.get("type") == "text":
                    text_value = item.get("text", "")
                    content_str += str(text_value) if text_value is not None else ""
            return content_str
        else:
            raise Exception(f"Unsupported content type: {type(content)}")

    @staticmethod
    def extract_reasoning_content(
        content: AllDatabricksContentValues | None,
    ) -> tuple[
        str | None,
        list[ChatCompletionThinkingBlock | ChatCompletionRedactedThinkingBlock] | None,
    ]:
        """
        Extract and return the reasoning content and thinking blocks
        """
        if content is None:
            return None, None
        thinking_blocks: list[ChatCompletionThinkingBlock | ChatCompletionRedactedThinkingBlock] | None = None
        reasoning_content: str | None = None
        if isinstance(content, list):
            for item in content:
                if item.get("type") == "reasoning":

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Coerce content to str before calling: str(value) for scalar values
  2. Wrap single content part dicts in a list: [{"type": "text", "text": ...}]
  3. Validate/sanitize your message array with a helper before sending to litellm

Example fix

# before
messages = [{"role": "user", "content": user_id}]  # int

# after
messages = [{"role": "user", "content": str(user_id)}]
Defensive patterns

Strategy: type-guard

Validate before calling

def normalize_messages(messages):
    for m in messages:
        c = m.get("content")
        if c is not None and not isinstance(c, (str, list)):
            m["content"] = str(c)
    return messages

Type guard

def has_valid_content(messages: list[dict]) -> bool:
    return all(
        m.get("content") is None or isinstance(m.get("content"), (str, list))
        for m in messages
    )

Try / catch

try:
    resp = litellm.completion(model=m, messages=messages)
except Exception as e:
    if "Unsupported content type" in str(e):
        messages = normalize_messages(messages)
        resp = litellm.completion(model=m, messages=messages)
    else:
        raise

Prevention

When it happens

Trigger: Passing messages whose content is an int, float, dict, or any non-str/non-list object, e.g. messages=[{"role": "user", "content": 42}] or a dict content block that is not a list of typed parts.

Common situations: Building messages dynamically from unvalidated user data; content set to a number from a template variable; a dict intended for the OpenAI content-parts API passed directly instead of being wrapped in a list.

Related errors


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