tiangolo/fastapi · error · ValueError

Expected UploadFile, received

Error message

Expected UploadFile, received: {type(__input_value)}

What it means

ValueError raised by UploadFile._validate, the pydantic validator registered for the UploadFile type via with_info_plain_validator_function. The validator (datastructures.py:135) requires the incoming value to be an instance of starlette UploadFile; anything else (str, bytes, dict, a file path, a SpooledTemporaryFile, etc.) is rejected because FastAPI/Starlette can only wrap a real uploaded file handle. Pydantic surfaces this ValueError as a request validation error.

Solutions

  1. Send the data as a real multipart file upload with the correct field name (requests files={...}, curl -F, httpx files=).
  2. Annotate the parameter as bytes or Annotated[bytes, File()] if you want raw bytes, or as a pydantic model / Form field if the input is not a file.
  3. In tests, open the file and pass the file object: files={'file': ('x.bin', open(path,'rb'), 'application/octet-stream')}.
  4. Verify the request Content-Type is multipart/form-data and the field name matches the parameter name.

Example fix

// before
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
# client sent JSON: {"file": "report.pdf"}  -> ValueError

// after
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
# client sends multipart:
#   httpx.post(url, files={"file": ("report.pdf", open("report.pdf","rb"))})
Defensive patterns

Strategy: type-guard

Validate before calling

# Ensure a real file object is used before constructing the request
import os
from starlette.datastructures import UploadFile

def make_files(name: str, path: str):
    f = open(path, "rb")
    assert isinstance(f, (object,)) and os.path.exists(path)
    return {name: (os.path.basename(path), f, "application/octet-stream")}

Type guard

from starlette.datastructures import UploadFile

def is_uploadable(value) -> bool:
    return isinstance(value, UploadFile)

Try / catch

from fastapi import HTTPException
from starlette.datastructures import UploadFile

async def take_file(value):
    if not isinstance(value, UploadFile):
        raise HTTPException(status_code=400, detail="a file upload is required")
    return await value.read()

Prevention

When it happens

Trigger: Declaring a path-operation parameter as UploadFile (or Annotated[UploadFile, File()]) but the request did not send a file part — e.g. the client sent a JSON string, plain bytes, a form field, or the test/client passed a non-file object. Also triggered by directly constructing/validating an UploadFile-typed model with a non-file input.

Common situations: Forgetting to upload an actual file (sending the filename string instead); test code that passes bytes to an UploadFile field instead of a file-like object; a proxy/middleware that replaced the upload with its raw contents; mismatched field name between client and endpoint.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/8693ca626d9c6114. Report an issue: GitHub.

Appendix: source

Thrown at fastapi/datastructures.py:135

        Any next read or write will be done from that position.

        To be awaitable, compatible with async, this is run in threadpool.
        """
        return await super().seek(offset)

    async def close(self) -> None:
        """
        Close the file.

        To be awaitable, compatible with async, this is run in threadpool.
        """
        return await super().close()

    @classmethod
    def _validate(cls, __input_value: Any, _: Any) -> "UploadFile":
        if not isinstance(__input_value, StarletteUploadFile):
            raise ValueError(f"Expected UploadFile, received: {type(__input_value)}")
        return cast(UploadFile, __input_value)

    @classmethod
    def __get_pydantic_json_schema__(
        cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
    ) -> dict[str, Any]:
        return {"type": "string", "contentMediaType": "application/octet-stream"}

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
    ) -> Mapping[str, Any]:
        from ._compat.v2 import with_info_plain_validator_function

        return with_info_plain_validator_function(cls._validate)


class DefaultPlaceholder:

View on GitHub (pinned to 3e8d1526d8)