BerriAI/litellm · error · Exception

Either file_data or file_id must be present in the file mess

Error message

Either file_data or file_id must be present in the file message: {message}

What it means

Anthropic file message validation: a message block of type 'file' reached the converter but its file object contains neither file_data (inline base64 bytes) nor file_id (an uploaded Files API id). Anthropic requires exactly one of these, so LiteLLM fails fast and echoes the message.

Source

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

                    type="url",
                    url=file_id,
                ),
            )
        elif content_block_type == "image":
            return_block_param = AnthropicMessagesImageParam(
                type="image",
                source=AnthropicContentParamSourceFileId(
                    type="file",
                    file_id=file_id,
                ),
            )
        elif content_block_type == "container_upload":
            return_block_param = AnthropicMessagesContainerUploadParam(type="container_upload", file_id=file_id)

        if return_block_param is None:
            raise Exception(f"Unable to parse anthropic file message: {message}")
        return return_block_param
    raise Exception(f"Either file_data or file_id must be present in the file message: {message}")


_EMPTY_TEXT_PLACEHOLDER: Final = "[System: Empty message content sanitised to satisfy protocol]"


def _sanitize_empty_text_content(
    message: AllMessageValues,
) -> AllMessageValues:
    """
    Case C: Sanitize empty text content
    - Replace empty or whitespace-only text content with a placeholder message.
    - Handles both string content and list-of-blocks content (rewriting only
      the empty text blocks in place; non-text blocks like images are left
      untouched).

    Returns:
        The message with sanitized content if needed, otherwise the original message
    """

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Set file_data with base64 bytes: {"type":"file","file":{"filename":"a.pdf","file_data":"data:application/pdf;base64,..."}}
  2. Or set file_id from the Anthropic Files API: upload the file first and pass the returned id
  3. If you intended inline content, use the source={type:'base64',...} form instead of a file block

Example fix

# before
content.append({"type": "file", "file": {"filename": "report.pdf"}})

# after
import base64
content.append({"type": "file", "file": {
    "filename": "report.pdf",
    "file_data": "data:application/pdf;base64," + base64.b64encode(open("report.pdf","rb").read()).decode(),
}})
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_file_message(block: dict) -> None:
    f = block.get("file") if isinstance(block, dict) else None
    if not (isinstance(f, dict) and (f.get("file_data") or f.get("file_id"))):
        raise ValueError(f"file block needs file_data or file_id: {block}")

Type guard

from typing import Any

def is_valid_anthropic_file_block(block: Any) -> bool:
    return (
        isinstance(block, dict)
        and block.get("type") == "file"
        and isinstance(block.get("file"), dict)
        and bool(block["file"].get("file_data") or block["file"].get("file_id"))
    )

Prevention

When it happens

Trigger: {"type": "file", "file": {"filename": "x.pdf"}} with no data or id; file objects built from partial dicts after JSON round-tripping dropped keys; forwarding a file block whose fields were renamed.

Common situations: Constructing file blocks by hand for PDF/image inputs; migrating from inline base64 sources ("source": {...}) to the newer "file" block shape and forgetting both fields; empty file uploads that failed upstream but still got appended.

Related errors


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