BerriAI/litellm · error · ValueError

file_id and file_data are both None

Error message

file_id and file_data are both None

What it means

A chat message content block with type='file' passed the earlier check (the 'file' sub-dict exists) but that dict contains neither 'file_id' nor 'file_data'. litellm needs at least one of these to build the provider request, so it raises before sending anything.

Solutions

  1. Pass file_id from a prior file upload (e.g. OpenAI files API id like 'file-abc123')
  2. Or pass base64 file_data inline
  3. Check exact key names: the id/data live inside the 'file' sub-object, not on the content block

Example fix

# before
{"type": "file", "file": {"format": "pdf"}}

# after
{"type": "file", "file": {"file_id": "file-abc123", "format": "pdf"}}
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_file_blocks(messages: list[dict]) -> None:
    for i, msg in enumerate(messages):
        content = msg.get("content")
        if not isinstance(content, list):
            continue
        for j, block in enumerate(content):
            if block.get("type") == "file":
                f = block.get("file") or {}
                if not f.get("file_id") and not f.get("file_data"):
                    raise ValueError(f"messages[{i}].content[{j}]: file block needs file_id or file_data")

Type guard

from typing import Any, TypedDict, TypeGuard

class ValidFileBlock(TypedDict):
    type: str
    file: dict[str, Any]

def is_valid_file_block(block: Any) -> TypeGuard[ValidFileBlock]:
    if not isinstance(block, dict) or block.get("type") != "file":
        return False
    f = block.get("file")
    return isinstance(f, dict) and bool(f.get("file_id") or f.get("file_data"))

# usage: blocks = [b for b in content if b.get("type") != "file" or is_valid_file_block(b)]

Try / catch

try:
    litellm.completion(model=model, messages=messages)
except ValueError as e:
    if "file_id and file_data are both None" in str(e):
        # fix upstream: attach the uploaded file id, then rebuild the message
        attach_file_id(messages)
    else:
        raise

Prevention

When it happens

Trigger: Sending a content block like {"type": "file", "file": {"format": "pdf"}} where both file_id and file_data are absent, None, or empty strings.

Common situations: Hand-building file message blocks and forgetting the id; uploading a file but reading the wrong key from the upload response; copy-pasting doc examples that omit file_id.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/afb4d736e27a12b8. Report an issue: GitHub.

Appendix: source

Thrown at litellm/litellm_core_utils/prompt_templates/common_utils.py:1376

    Migrate file to image_url
    """
    from litellm.types.llms.openai import (
        ChatCompletionImageObject,
        ChatCompletionImageUrlObject,
    )

    file_sub: Final = message.get("file")
    if file_sub is None:
        raise litellm.BadRequestError(
            message="Content block has type='file' but is missing the required 'file' field",
            model=None,
            llm_provider=None,
        )
    file_id: Final = file_sub.get("file_id")
    file_data: Final = file_sub.get("file_data")
    format: Final = file_sub.get("format")
    if not file_id and not file_data:
        raise ValueError("file_id and file_data are both None")
    image_url_object: Final = ChatCompletionImageObject(
        type="image_url",
        image_url=ChatCompletionImageUrlObject(
            url=cast(str, file_id or file_data),
        ),
    )
    if format and isinstance(image_url_object["image_url"], dict):
        image_url_object["image_url"]["format"] = format
    return image_url_object


def get_last_user_message(messages: list[AllMessageValues]) -> str | None:
    """
    Get the last consecutive block of messages from the user.

    Example:
    messages = [
        {"role": "user", "content": "Hello, how are you?"},

View on GitHub (pinned to 77b7c6c40c)