BerriAI/litellm · error · ValueError

extract_file_data does not accept bare str inputs. Pass…

Error message

extract_file_data does not accept bare str inputs. Pass bytes, an open file handle, a (filename, content) tuple, or a pathlib.Path. To upload a local file from a path, call open(path, 'rb') yourself.

What it means

extract_file_data() normalizes file inputs for multimodal messages and rejects bare str inputs. The source comment gives the reason: behind the LiteLLM proxy a str arrives from an attacker-controlled HTTP form field, and interpreting it as a filesystem path would be an arbitrary file read on the proxy host - so strings are refused outright. Accepted inputs are bytes, an open file handle (anything with .read), ContentFile-like objects, a (filename, content) tuple, or a pathlib.Path (a type an HTTP form cannot fabricate).

Solutions

  1. Encode text content: file_content.encode("utf-8"); base64.b64decode(...) for base64 strings
  2. For local files pass a handle (open(path, "rb")) or a pathlib.Path - both are accepted
  3. Use the tuple form: ("doc.pdf", file_bytes)
  4. Proxy operators: keep the contract that clients upload real content, never server-side paths

Example fix

# before
messages = [{
    "role": "user",
    "content": [{"type": "file", "file": {"file_content": "@notes.txt"}}],
}]  # str -> ValueError

# after
from pathlib import Path
messages = [{
    "role": "user",
    "content": [{"type": "file", "file": {"file_content": Path("notes.txt")}}],
}]
# or: {"file_content": ("notes.txt", Path("notes.txt").read_bytes())}
Defensive patterns

Strategy: type-guard

Validate before calling

from pathlib import Path

def normalize_file_input(v):
    if isinstance(v, str):
        return v.encode("utf-8")  # treat as text content, never a path
    return v

msg = [{"type": "file", "file": {"file_content": normalize_file_input(file_content)}}]

Type guard

from os import PathLike

def is_accepted_file_input(v: object) -> bool:
    if isinstance(v, str):
        return False
    return isinstance(v, (bytes, PathLike, tuple)) or hasattr(v, "read")

Prevention

When it happens

Trigger: Passing file content or a path as a plain string in a message part, e.g. {"type": "file", "file": {"file_content": "/etc/passwd"}} or a base64 str; upgrading litellm to a version with this hardening and re-running code that used to send strings.

Common situations: Version upgrade breaking callers that passed paths or raw/base64 strings (deliberate behavior change); proxy file-upload endpoints receiving form-field strings; SDK users migrating from a str-based convenience pattern to the typed contract.

Related errors


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

Appendix: source

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

        elif len(file_data) == 3:
            filename, file_content, content_type = file_data
        elif len(file_data) == 4:
            filename, file_content, content_type, file_headers = file_data
    elif isinstance(file_data, InMemoryFile):
        filename = file_data.name
        file_content = file_data
        content_type = file_data.content_type
    else:
        file_content = file_data
    # Convert content to bytes
    if isinstance(file_content, str):
        # Bare string inputs are rejected: when this helper runs in a proxy
        # request handler the string came from an attacker-controlled form
        # field, and opening it as a path is an arbitrary file read on the
        # proxy host. SDK callers who want to upload from a path should
        # either pass a pathlib.Path (a PathLike instance — see the branch
        # below) or open the file themselves and pass the handle / bytes.
        raise ValueError(
            "extract_file_data does not accept bare str inputs. Pass bytes, "
            "an open file handle, a (filename, content) tuple, or a "
            "pathlib.Path. To upload a local file from a path, call "
            "open(path, 'rb') yourself."
        )
    if isinstance(file_content, PathLike):
        # PathLike (pathlib.Path) is a Python-level type that HTTP form
        # values can't fabricate. Treat as a local file path for SDK
        # convenience.
        if filename is None:
            filename = Path(file_content).name
        with open(file_content, "rb") as f:
            content = f.read()
    elif isinstance(file_content, io.IOBase):
        # If it's a file-like object
        # Try to get filename from file handle if not already set
        if not filename and hasattr(file_content, "name"):
            filename = Path(file_content.name).name

View on GitHub (pinned to 77b7c6c40c)