unslothai/unsloth · error · ValueError

unsupported content block type {btype!r} in a user message

Error message

unsupported content block type {btype!r} in a user message

What it means

Raised by an Anthropic messages-request validator when a user message's content list contains a block whose type is not in _KNOWN_ANTHROPIC_BLOCK_TYPES, or whose type is not a string at all (the isinstance check also prevents an unhashable type value raising TypeError and escaping as a 500). It converts unsupported/absent block types into a clean 400.

Source

Thrown at studio/backend/models/inference.py:2541

        if not isinstance(data, dict):
            return data
        content = data.get("content")
        if data.get("role") == "assistant":
            # Coerce only an explicit null (resumed tool-only turn). A missing
            # content key stays malformed so the required-field check still 400s.
            if "content" in data and content is None:
                return {**data, "content": ""}
            return data
        if isinstance(content, list):
            for block in content:
                btype = (
                    block.get("type") if isinstance(block, dict) else getattr(block, "type", None)
                )
                # Guard the value: a non-string type is unsupported too, and a
                # membership test on an unhashable value would raise TypeError
                # (escaping as a 500 instead of a clean 400).
                if not isinstance(btype, str) or btype not in _KNOWN_ANTHROPIC_BLOCK_TYPES:
                    raise ValueError(f"unsupported content block type {btype!r} in a user message")
        return data


class AnthropicTool(BaseModel):
    # User-defined client tools have input_schema; Anthropic-schema client tools
    # and server tools use type/name.
    type: Optional[str] = None
    name: Optional[str] = None
    description: Optional[str] = None
    input_schema: Optional[dict] = None
    model_config = {"extra": "allow"}


class AnthropicMessagesRequest(BaseModel):
    model: str = "default"
    max_tokens: Optional[int] = None
    messages: list[AnthropicMessage]
    system: Optional[Union[str, list]] = None

View on GitHub (pinned to 203007d190)

Solutions

  1. Restrict user-message blocks to the supported types (text, image, tool_use, tool_result — check _KNOWN_ANTHROPIC_BLOCK_TYPES in the repo).
  2. Convert documents/PDFs to text blocks client-side before sending.
  3. Validate every block has a string 'type' key before serialization.

Example fix

# before
content = [{"type": "document", "source": {...}}]  # unsupported here

# after
content = [{"type": "text", "text": extracted_document_text}]
Defensive patterns

Strategy: validation

Validate before calling

KNOWN = {'text', 'image', 'tool_use', 'tool_result'}  # mirror _KNOWN_ANTHROPIC_BLOCK_TYPES
def valid_user_blocks(content) -> bool:
    if not isinstance(content, list):
        return True
    return all(
        isinstance(b, dict) and isinstance(b.get('type'), str) and b['type'] in KNOWN
        for b in content
    )

Type guard

def is_supported_block(b) -> bool:
    return isinstance(b, dict) and isinstance(b.get('type'), str) and b['type'] in {
        'text', 'image', 'tool_use', 'tool_result'
    }

Prevention

When it happens

Trigger: POST /v1/messages with user content [{"type": "document", ...}], [{"type": 123}], or [{}] (missing type). Sending Anthropic server-tool types the backend has not modeled also triggers it.

Common situations: Porting a client from Anthropic's full API which supports more block types (document, thinking, redacted_thinking, server tools) than this backend models; typos in block type strings; blocks built dynamically where type ends up None or non-string.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/6ab9eff11549798a. Report an issue: GitHub.